web-dev-qa-db-fra.com

Deserialize json array stream un élément à la fois

Je sérialise un tableau d'objets volumineux en un flux de réponse json http. Maintenant, je veux désérialiser ces objets du flux un à un. Existe-t-il des bibliothèques c # qui me permettent de le faire? J'ai jeté un œil sur json.net mais il me semble que je devrais désérialiser la gamme complète d'objets à la fois.

[{large json object},{large json object}.....]

Précision: je veux lire un objet JSON du flux à la fois et le désérialiser.

30
ZNS

Pour lire le JSON de manière incrémentielle, vous devez utiliser une variable JsonTextReader en combinaison avec une variable StreamReader. Cependant, vous ne devez pas nécessairement lire tous les fichiers JSON manuellement à partir du lecteur. Vous devriez pouvoir utiliser l'API Linq-To-JSON pour charger chaque objet volumineux depuis le lecteur afin de pouvoir l'utiliser plus facilement.

Pour un exemple simple, disons que j'ai un fichier JSON qui ressemble à ceci:

[
  {
    "name": "foo",
    "id": 1
  },
  {
    "name": "bar",
    "id": 2
  },
  {
    "name": "baz",
    "id": 3
  }
]

Le code permettant de le lire progressivement à partir du fichier peut ressembler à ce qui suit. (Dans votre cas, vous remplacerez FileStream par votre flux de réponses.)

using (FileStream fs = new FileStream(@"C:\temp\data.json", FileMode.Open, FileAccess.Read))
using (StreamReader sr = new StreamReader(fs))
using (JsonTextReader reader = new JsonTextReader(sr))
{
    while (reader.Read())
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            // Load each object from the stream and do something with it
            JObject obj = JObject.Load(reader);
            Console.WriteLine(obj["id"] + " - " + obj["name"]);
        }
    }
}

La sortie de ce qui précède ressemblerait à ceci:

1 - foo
2 - bar
3 - baz
44
Brian Rogers

J'ai simplifié l'un des échantillons/tests de mon analyseur/désérialiseur pour répondre plus facilement au cas d'utilisation de cette question.

Voici pour les données de test:

https://github.com/ysharplanguage/FastJsonParser/tree/master/JsonTest/TestData

(cf. pères.json.txt)

Et voici l'exemple de code:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Text;

    // Our stuff
    using System.Text.Json;

//...

    public class FathersData
    {
        public Father[] fathers { get; set; }
    }

    public class Someone
    {
        public string name { get; set; }
    }

    public class Father : Someone
    {
        public int id { get; set; }
        public bool married { get; set; }
        // Lists...
        public List<Son> sons { get; set; }
        // ... or arrays for collections, that's fine:
        public Daughter[] daughters { get; set; }
    }

    public class Child : Someone
    {
        public int age { get; set; }
    }

    public class Son : Child
    {
    }

    public class Daughter : Child
    {
        public string maidenName { get; set; }
    }

//...

    static void FilteredFatherStreamTestSimplified()
    {
        // Get our parser:
        var parser = new JsonParser();

        // (Note this will be invoked thanks to the "filters" dictionary below)
        Func<object, object> filteredFatherStreamCallback = obj =>
        {
            Father father = (obj as Father);
            // Output only the individual fathers that the filters decided to keep (i.e., when obj.Type equals typeof(Father)),
            // but don't output (even once) the resulting array (i.e., when obj.Type equals typeof(Father[])):
            if (father != null)
            {
                Console.WriteLine("\t\tId : {0}\t\tName : {1}", father.id, father.name);
            }
            // Do not project the filtered data in any specific way otherwise,
            // just return it deserialized as-is:
            return obj;
        };

        // Prepare our filter, and thus:
        // 1) we want only the last five (5) fathers (array index in the resulting "Father[]" >= 29,995),
        // (assuming we somehow have prior knowledge that the total count is 30,000)
        // and for each of them,
        // 2) we're interested in deserializing them with only their "id" and "name" properties
        var filters = 
            new Dictionary<Type, Func<Type, object, object, int, Func<object, object>>>
            {
                // We don't care about anything but these 2 properties:
                {
                    typeof(Father), // Note the type
                    (type, obj, key, index) =>
                        ((key as string) == "id" || (key as string) == "name") ?
                        filteredFatherStreamCallback :
                        JsonParser.Skip
                },
                // We want to pick only the last 5 fathers from the source:
                {
                    typeof(Father[]), // Note the type
                    (type, obj, key, index) =>
                        (index >= 29995) ?
                        filteredFatherStreamCallback :
                        JsonParser.Skip
                }
            };

        // Read, parse, and deserialize fathers.json.txt in a streamed fashion,
        // and using the above filters, along with the callback we've set up:
        using (var reader = new System.IO.StreamReader(FATHERS_TEST_FILE_PATH))
        {
            FathersData data = parser.Parse<FathersData>(reader, filters);

            System.Diagnostics.Debug.Assert
            (
                (data != null) &&
                (data.fathers != null) &&
                (data.fathers.Length == 5)
            );
            foreach (var i in Enumerable.Range(29995, 5))
                System.Diagnostics.Debug.Assert
                (
                    (data.fathers[i - 29995].id == i) &&
                    !String.IsNullOrEmpty(data.fathers[i - 29995].name)
                );
        }
        Console.ReadKey();
    }

Le reste des bits est disponible ici:

https://github.com/ysharplanguage/FastJsonParser

'HTH,

4
YSharp

Ceci est ma solution (combinée à partir de sources différentes, mais principalement basée sur Brian Rogers solution) pour convertir un fichier JSON énorme (qui est un tableau d’objets) en fichier XML pour tout objet générique.

JSON ressemble à ceci:

   {
      "Order": [
          { order object 1},
          { order object 2},
          {...}
          { order object 10000},
      ]
   }

XML de sortie:

<Order>...</Order>
<Order>...</Order>
<Order>...</Order>

Code C #:

XmlWriterSettings xws = new XmlWriterSettings { OmitXmlDeclaration = true };
using (StreamWriter sw = new StreamWriter(xmlFile))
using (FileStream fs = new FileStream(jsonFile, FileMode.Open, FileAccess.Read))
using (StreamReader sr = new StreamReader(fs))
using (JsonTextReader reader = new JsonTextReader(sr))
{
    //sw.Write("<root>");
    while (reader.Read())
    {
        if (reader.TokenType == JsonToken.StartArray)
        {
            while (reader.Read())
            {
                if (reader.TokenType == JsonToken.StartObject)
                {
                    JObject obj = JObject.Load(reader);
                    XmlDocument doc = JsonConvert.DeserializeXmlNode(obj.ToString(), "Order");
                    sw.Write(doc.InnerXml); // a line of XML code <Order>...</Order>
                    sw.Write("\n");
                    //this approach produces not strictly valid XML document
                    //add root element at the beginning and at the end to make it valid XML                                
                }
            }
        }
    }
    //sw.Write("</root>");
}
0
serop

Avec Cinchoo ETL - une bibliothèque open source, vous pouvez analyser efficacement des fichiers JSON volumineux avec une mémoire réduite. Puisque les objets sont construits et retournés dans un modèle d'extraction basé sur un flux

using (var p = new ChoJSONReader(** YOUR JSON FILE **))
{
            foreach (var rec in p)
            {
                Console.WriteLine($"Name: {rec.name}, Id: {rec.id}");
            }
}

Pour plus d'informations, s'il vous plaît visitez l'article codeproject.

J'espère que ça aide.

0
RajN