10

I am reading a NetworkStream for json string and then deserializing it using Newtonsoft.Json.

Sometimes, two json objects could be sent back-to-back and read at the same time on the stream. But the Newtonsoft.Json serializer gives me only one object.

For example, if I have the following string on the stream:

{"name":"John Doe","age":10}{"name":"Jane Doe","age":10}

If I deserialize the stream, the serializer reads the entire stream, but gives only the first object.

Is there a way to make the serializer read only the first object from the stream and then read the next object in the next iteration of a loop?

Code:

public static Person Deserialize(Stream stream)
{
    var Serializer = new JsonSerializer();
    var streamReader = new StreamReader(stream, new UTF8Encoding());
    return Serializer.Deserialize<Person>(new JsonTextReader(streamReader));
}

I cannot deserialize as a list because I'm not receiving a json array.

Steve Friedl
  • 3,929
  • 1
  • 23
  • 30
Xpleria
  • 5,472
  • 5
  • 52
  • 66
  • Can you include code you are using now for deserialization? – Evk Feb 20 '18 at 10:13
  • I found what seems to be a rather good Wikipedia page on the subject: https://en.wikipedia.org/wiki/JSON_streaming, quite general but still. – Peter B Feb 20 '18 at 10:15
  • @Evk I've updated my question. – Xpleria Feb 20 '18 at 10:18
  • @PeterB Nice article, thanks! From the article, looks like the `json` I'm dealing with is `concatenated json`. Which is a good start. Now I need a way to do this in `Json.net` – Xpleria Feb 20 '18 at 10:19
  • There is a possible answer here: https://stackoverflow.com/questions/43747477/how-to-parse-huge-json-file-as-stream-in-json-net. It works by skipping to the next `{` and parse an object from there, – Palle Due Feb 20 '18 at 10:19
  • Hackcode: Could you not break into a list of strings via a delimiter? .Replace("}{", "},{").. then break. Thus pushing into the de-serializer ? – Tez Wingfield Feb 20 '18 at 10:21
  • What happens if you try to deserialize from the stream again? It occurs to me that if it is only returning one object (which makes sense) then the stream may well still contain your second object... – Chris Feb 20 '18 at 10:21
  • @Chris There is nothing left on the stream once it deserializes the first object. To confirm this, I used a `MemoryStream` in debug mode and it reads the entire stream. – Xpleria Feb 20 '18 at 10:22
  • Related question: https://stackoverflow.com/q/44738862/1220550 – Peter B Feb 20 '18 at 10:23
  • @PalleDue The `json` string in that question is a valid json array. My json string is not an array. At fist glance, it does not look like it would work in my case. Nevertheless, will give it a try and check. – Xpleria Feb 20 '18 at 10:25
  • Related or duplicate: [Line delimited json serializing and de-serializing](https://stackoverflow.com/q/29729063/3744182). – dbc Feb 20 '18 at 11:34

2 Answers2

11

I think you can do it like this:

public static IList<Person> Deserialize(Stream stream) {
    var serializer = new JsonSerializer();
    var streamReader = new StreamReader(stream, new UTF8Encoding());
    var result = new List<Person>();
    using (var reader = new JsonTextReader(streamReader)) {
        reader.CloseInput = false;
        // important part
        reader.SupportMultipleContent = true;
        while (reader.Read()) {
            result.Add(serializer.Deserialize<Person>(reader));
        }
    }
    return result;
}

Important part is SupportMultipleContent property, which notifies reader that there might be multiple json objects side to side.

Evk
  • 98,527
  • 8
  • 141
  • 191
0

you can try it doing like this

        var httpRequest = HttpContext.Current.Request;
        // This list will have all the stream objects
        var persons = new List<Person>();
        if (httpRequest.Files.Count > 0)
        {
            for (var obj = 0; doc < httpRequest.Files.Count; obj++)
            {
                var postedFile = httpRequest.Files[obj];
                var bytes = new byte[postedFile.ContentLength];
                postedFile.InputStream.Read(bytes, 0, postedFile.ContentLength);
                persons.Add(Serializer.Deserialize<Person>(new JsonTextReader(new StreamReader(new MemoryStream(bytes)))));
            }
        }
Ghulam Mohayudin
  • 1,093
  • 10
  • 18
  • 1
    Your solution will definitely work, but it's inefficient in many use cases. It requires the content to be completely retrieved, before the processing starts. It may also require high RAM upfront, if your entities are big. – mlemanczyk Feb 20 '22 at 12:58