0

Using C# and specifically JSON.net how to do I deal with JSON object that can have a dynamic value. For example:

{ "message_id":"123-456", "data":["data","list"], "type":"MSG_TYPE1" }

{ "message_id":"123-457", "data":"my data string", "type":"MSG_TYPE2" }

{ "message_id":"123-458", "data":{"key1":"value1","key2":"value2"}, "type":"MSG_TYPE3" }

The "data" value can be any type. In C#, I defined a ServiceMessage class that contains these properties but what type of property should "data" be. I was looking at a JToken or JContainer but I'm not sure the best way to implement.

public class ServiceMessage
{
    public string message_id { get; set; }
    public JContainer data { get; set; }
    public string type { get; set; }

    public string getJSON()
    {
        string json = JsonConvert.SerializeObject(this);
        return json;
    }

    public void setJSON(string json)
    {
            dynamic jsonObj = JsonConvert.DeserializeObject(json);

            this.message_id = jsonObj.message_id;
            this.type = jsonObj.type;
            this.data = // what goes here.
    }
}
Hugh
  • 83
  • 1
  • 11
  • do you know they actual structure of the data object at compile time or will each message contain different values? how do you expect to do the lookup for the data value? – dbarnes Jan 24 '15 at 00:41
  • I will know the actual structure of the data object based on the message type. If I know that the message is of MSG_TYPE1 then I will know the object structure. – Hugh Jan 26 '15 at 15:16
  • @Huge it's kind of difficult to have a strongly typed object without knowing the exact way you plan to access the object you can do something with generics but I'm pretty sure it will cause more trouble than it's worth. How do you expect to access the data once it comes over the wire? – dbarnes Jan 26 '15 at 18:12

2 Answers2

0

What are you doing with the data? It could make sense to leave it as a json string and handle it when the data is needed.

aminner
  • 359
  • 3
  • 10
  • Once the message id and message type are known then the data element is retrieved. The JSON is coming from a python producer (but it could be anything). I was thinking of keeping data as a JSON string and then handle when needed but I wasn't sure if there was a better way? – Hugh Jan 23 '15 at 23:24
  • http://stackoverflow.com/questions/13652983/dynamic-jcontainer-json-net-iterate-over-properties-at-runtime has some information - but based on what your objects currently look like. I would leave it as a string due to the fact that you'll end up having to deserialize anyway. At least when you pull it out by message type, I'm assuming at least, the structure will follow a set pattern and allow you to continue as you were for the initial. – aminner Jan 24 '15 at 01:14
  • Thank you. So being a newbie at this, I was deserializing "data" and it seems like JSON.Net detects the JSON array, object, etc. So how do I make sure that particular field remains as a string? – Hugh Jan 26 '15 at 15:20
  • So doing some investigation it looks like I might need a custom converter to do the partial deserialization for me. – Hugh Jan 26 '15 at 15:41
0

Well I'm not sure if this is proper or not but this is what I came up with. I borrowed heavily from here

class ServiceMessageConverter : CustomCreationConverter<ServiceMessage>
{

    public override ServiceMessage Create(Type objectType)
    {
        return new ServiceMessage();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var msg = new ServiceMessage();
        string data_string = "";

        //get an array of the object's props so I can check if the JSON prop s/b mapped to it
        var objProps = objectType.GetProperties().Select(p => p.Name.ToLower()).ToArray();

        while (reader.Read())
        {
            if (reader.Value == null)
            {
                continue;
            }
            // read the property name
            string readerValue = reader.Value.ToString().ToLower();

            // read the property value
            if (reader.Read())
            {
                // make sure the complex types are saved as strings
                if (readerValue.ToLower() == "data")
                {
                    if (reader.TokenType == JsonToken.StartObject)
                    {
                        dynamic data_obj = serializer.Deserialize(reader);
                        data_string = data_obj.ToString();
                    }
                    else if (reader.TokenType == JsonToken.StartArray)
                    {
                        dynamic data_obj = serializer.Deserialize(reader);
                        data_string = data_obj.ToString();
                    }
                    else
                    {
                        data_string = reader.Value.ToString();
                    }
                    // stuff the data element value into the ServiceMessage
                    PropertyInfo pi = msg.GetType().
                        GetProperty("data", BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                    pi.SetValue(msg, data_string);
                }
                else
                {
                    // stuff the value into the ServiceMessage
                    PropertyInfo pi = msg.GetType().
                        GetProperty(readerValue, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                    var convertedValue = Convert.ChangeType(reader.Value, pi.PropertyType);
                    pi.SetValue(msg, convertedValue, null);
                }

            }
        }
        return msg;

    }
}
Hugh
  • 83
  • 1
  • 11