7
\$\begingroup\$

I'm trying to turn an array of bytes into a C# object. I only the know the type of the object at runtime.

Right now, I'm using the JsonConvert.DeserializeObject<T> to do this, but the result is clunky, mostly because I don't know T at compile time.

If I knew T at compile time, I could simply do:

string json = System.Text.Encoding.UTF8.GetString(buffer);
return JsonConvert.DeserializeObject<MyType>(json);

But since I don't, I must do:

string json = System.Text.Encoding.UTF8.GetString(buffer);
var deserializeInfoMethodInfo = typeof(JsonConvert).GetMethods().Where(method => method.Name == "DeserializeObject" && method.IsGenericMethod).First();
return deserializeInfoMethodInfo.MakeGenericMethod(paramInfo.ParameterType).Invoke(null, new object[] { json });

Is there a way to make this simpler?

I have experimented with something like this:

using (MemoryStream stream = new MemoryStream(buffer, bufferIndex, paramLengths[i])) {
    using (JsonTextReader jsonReader = new JsonTextReader(new StreamReader(stream))) {
        var serializer = new JsonSerializer();
        return serializer.Deserialize(jsonReader, paramInfo.ParameterType);
    }
}

This is arguably more readable, but I don't like how I have to get the buffer (which I got from calling stream.Read(buffer, 0, length) in another place), turn it back into a stream, and then have the serializer iterate through the stream again.

Is there a way to trim down the second snippet, or should I stop whining and use the third?

\$\endgroup\$
10
\$\begingroup\$

I think, it can be simplified just a little bit:

static object Deserialize(byte[] buffer, Type type)
{
  using (StreamReader sr = new StreamReader(new MemoryStream(buffer)))
  {
    return JsonConvert.DeserializeObject(sr.ReadToEnd(), type);
  }
}

NB: StreamReader releases/disposes MemoryStream, so you don't have to encapsulate that in a using()


Or You could even do:

static object Deserialize(byte[] buffer, Type type)
{
  return JsonConvert.DeserializeObject(System.Text.Encoding.UTF8.GetString(buffer), type);
}

or:

static object Deserialize(byte[] buffer, Type type)
{
  return JToken.Parse(Encoding.UTF8.GetString(buffer)).ToObject(type);
}
\$\endgroup\$
  • 1
    \$\begingroup\$ No idea why that DeserializeObject overload slipped out of my mind. I'll use your second example. Thanks \$\endgroup\$ – pushkin Apr 21 '18 at 16:04
  • 1
    \$\begingroup\$ @pushkin but you are aware of that with the second example you're reading the same data (at least) three times? 1) from source to buffer 2) from buffer to string 3) from string to parse --- as I wrote in my answer, you're streams are incorrectly chained... \$\endgroup\$ – t3chb0t Apr 21 '18 at 17:20
10
\$\begingroup\$
using (MemoryStream stream = new MemoryStream(buffer, bufferIndex, paramLengths[i])) {
    using (JsonTextReader jsonReader = new JsonTextReader(new StreamReader(stream))) {
        var serializer = new JsonSerializer();
        return serializer.Deserialize(jsonReader, paramInfo.ParameterType);
    }
}

This is exactly as it should be done. Using reflection as if in your first example isn't optimal because if there is an easy way to avoid it (and there is) you should not make it more complicted.

You could however clean this up a bit.

using (var memoryStream = new MemoryStream(buffer, bufferIndex, paramLengths[i])) 
using (var streamReader = new StreamReader(memoryStream))
using (var jsonReader = new JsonTextReader(streamReader))
{
    var serializer = new JsonSerializer();
    return serializer.Deserialize(jsonReader, paramInfo.ParameterType);    
}
  • usings don't have to be nested
  • the streamReader needs to be disposed too

I don't like how I have to get the buffer (which I got from calling stream.Read(buffer, 0, length) in another place), turn it back into a stream, and then have the serializer iterate through the stream again.

Since we don't know anything about the process before parsing JSON, it's not possible to give you any other advice. However, I'm pretty sure you don't have to read it twice, especially that you are working with streams and they can usually be connected in such a way that allows you do things only once.

\$\endgroup\$
  • \$\begingroup\$ I believe the streamReader using is unnecessary, because JsonTextReader will take ownership of it and dispose of it when it's disposed: see here. I saw this mentioned in a couple of other places too \$\endgroup\$ – pushkin Apr 21 '18 at 15:50
  • 2
    \$\begingroup\$ @pushkin true, some streams will take ownership and dispose the substream but it's safer to not trust this mechanism because in order to use it you need to know the internals or crefully read the documentation. It's always better to take care of streams you own/create yourself. \$\endgroup\$ – t3chb0t Apr 21 '18 at 16:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.