wake-up-neo.com

Deserialisieren Sie den Json-Array-Stream einzeln

Ich serialisiere ein Array von großen Objekten zu einem HTTP-Antwortstrom von Json. Jetzt möchte ich diese Objekte einzeln aus dem Stream deserialisieren. Gibt es irgendwelche c # -Bibliotheken, die mir dies erlauben? Ich habe mir json.net angesehen, aber es scheint, als müsste ich das gesamte Array von Objekten auf einmal deserialisieren.

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

Klarstellung: Ich möchte jeweils ein Json-Objekt aus dem Stream lesen und es deserialisieren.

30
ZNS

Um die JSON inkrementell lesen zu können, müssen Sie eine JsonTextReader in Kombination mit einer StreamReader verwenden. Sie müssen jedoch nicht notwendigerweise alle JSON-Dateien manuell aus dem Reader lesen. Sie sollten in der Lage sein, die Linq-To-JSON-API zu verwenden, um jedes große Objekt aus dem Reader zu laden, damit Sie einfacher damit arbeiten können.

Ein einfaches Beispiel: Ich hatte eine JSON-Datei, die folgendermaßen aussah:

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

Code, der inkrementell aus der Datei gelesen wird, sieht in etwa wie folgt aus. (In Ihrem Fall würden Sie den FileStream durch Ihren Antwortstream ersetzen.)

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"]);
        }
    }
}

Die Ausgabe des obigen würde folgendermaßen aussehen:

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

Ich habe eines der Beispiele/Tests meines Parsers/Deserializers vereinfacht, um den Anwendungsfall dieser Frage einfacher zu beantworten.

Hier ist für die Testdaten:

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

(vgl. fathers.json.txt)

Und hier ist der Beispielcode:

    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();
    }

Der Rest der Bits ist hier verfügbar:

https://github.com/ysharplanguage/FastJsonParser

'HTH,

4
YSharp

Dies ist meine Lösung (kombiniert aus verschiedenen Quellen, aber hauptsächlich basierend auf Brian Rogers Lösung), um riesige JSON-Dateien (die ein Array von Objekten sind) in XML-Dateien für generische Objekte zu konvertieren.

JSON sieht so aus:

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

Ausgabe-XML:

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

C # -Code:

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

Mit Cinchoo ETL - einer Open Source-Bibliothek können Sie große JSON-Dateien effizient und mit geringem Speicherbedarf analysieren. Da die Objekte in einem Stream-basierten Pull-Modell erstellt und zurückgegeben werden

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

Weitere Informationen finden Sie im Artikel zum Codeprojekt.

Ich hoffe es hilft.

0
RajN