1

I'm parsing this JSON in .net with newtonsoft.json and using json2csharp.com to get the classes I need.

{
"rval": 0,
"msg_id": 3,
"param": [{ "camera_clock": "2015-09-03 04:42:20" },
          { "video_standard": "NTSC" },
          { "app_status": "idle" }
          // there are 30+ properties structured that way
         ]
}

json2csharp gives me:

public int rval { get; set; }
public int msg_id { get; set; }
public List<Param> param { get; set; }

class Param 
{
     public string camera_clock {get;set;}
     public string video_standard {get;set;}
     public string app_status {get;set;}
     // 30+ more
}

And the Param object contains all the params. So whenever I deserialize it I get 31 List<Param> objects with all the properties empty except for one. What i'm looking for is get one Param object with all the 31 properties set.

Unfortunately I can't change JSON format to something like following (which reflects how I want to read it):

"param": { 
   "camera_clock": "2015-09-03 04:42:20",
   "video_standard": "NTSC" ,
   "app_status": "idle" 
   // there are 30+ properties structured that way
 }
dbc
  • 104,963
  • 20
  • 228
  • 340
kbronctjr
  • 57
  • 7
  • 4
    According to the structure of the JSON, the `"param"` property is an array of 32 objects with one property each. So what Json2CSharp is giving you is correct. If you can't control how that JSON is created, then you will need to write a custom deserializer. They are very easy in Newtonsoft. – krillgar Jan 28 '16 at 18:13
  • Can you control the outputted json, i mean are you able to request another structure? – STORM Jan 28 '16 at 18:15
  • If you're stuck with the JSON structure, you might need to do an additional map step to get it in the correct format. Or deserialize it into a dynamic object and map it. – lintmouse Jan 28 '16 at 18:16
  • @krillgar yeah, I can't control that json. I don't know what kind of genious wrote that json this way.... I will try to write a custom deserializer, but I've never done it. Thanks! – kbronctjr Jan 28 '16 at 18:16
  • I've dealt with far worse JSON that isn't valid outside of "well Ruby lets us return it that way", and the Deserializers work wonderfully. – krillgar Jan 28 '16 at 18:18
  • If the set of all possible parameters is not known in advance, you could make `param` be a `List>`. – dbc Jan 28 '16 at 18:19
  • @dbc the parameters are allways the same, so i prefer to access each one of them as a property – kbronctjr Jan 28 '16 at 18:22
  • just an fyi, http://stackoverflow.com/a/8031283/1365053 this is how you create a custom deserializer – Bilal Fazlani Jan 28 '16 at 18:28
  • @BilalFazlani Thanks! I will have a look at it when I can – kbronctjr Jan 28 '16 at 18:29
  • you could deserialize it to a Dictionary using the property name as the key. From those 3, string would work but in the set of 30+, there could be more than just that data type (at which point, I would not do that) – Ňɏssa Pøngjǣrdenlarp Jan 28 '16 at 18:54

2 Answers2

2

If you want your Param object to have a fixed set of properties corresponding to the array of properties shown in your JSON, you're going to need to write a custom JsonConverter that bubbles the properties out of the array and into a single object. Thus:

public class ArrayToObjectConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;

        if (existingValue == null)
        {
            var contract = serializer.ContractResolver.ResolveContract(objectType);
            existingValue = contract.DefaultCreator();
        }

        switch (reader.TokenType)
        {
            case JsonToken.StartArray:
                {
                    var jArray = JArray.Load(reader);

                    var jObj = new JObject();
                    foreach (var prop in jArray.OfType<JObject>().SelectMany(o => o.Properties()))
                        jObj.Add(prop);

                    using (var sr = jObj.CreateReader())
                    {
                        serializer.Populate(sr, existingValue);
                    }
                }
                break;

            case JsonToken.StartObject:
                serializer.Populate(reader, existingValue);
                break;

            default:
                var msg = "Unexpected token type " + reader.TokenType.ToString();
                Debug.WriteLine(msg);
                throw new JsonSerializationException(msg);
        }

        return existingValue;
    }

    public override bool CanWrite { get { return false; } }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Then use it like:

        var settings = new JsonSerializerSettings { Converters = new[] { new ArrayToObjectConverter<Param>() } };
        var root = JsonConvert.DeserializeObject<RootObject>(jsonString, settings);

Note that I did not do (re-)serialization as an array of objects, as your question doesn't ask for it.

The answer uses the following class definitions:

public class Param
{
    public string camera_clock { get; set; }
    public string video_standard { get; set; }
    public string app_status { get; set; }
    public string stream_out_type { get; set; }
    public string save_low_resolution_clip { get; set; }
    public string video_resolution { get; set; }
    public string video_stamp { get; set; }
    public string video_quality { get; set; }
    public string timelapse_video { get; set; }
    public string photo_size { get; set; }
    public string photo_stamp { get; set; }
    public string photo_quality { get; set; }
    public string timelapse_photo { get; set; }
    public string selfie_photo { get; set; }
    public string burst_photo { get; set; }
    public string autoshoot_photo { get; set; }
    public string loop_record { get; set; }
    public string motion_detec_video { get; set; }
    public string status_led_switch { get; set; }
    public string wifi_led_switch { get; set; }
    public string osd_switch { get; set; }
    public string cardvr_switch { get; set; }
    public string delay_pwroff { get; set; }
    public string rotate_image { get; set; }
    public string mic_vol { get; set; }
    public string language { get; set; }
    public string date_disp_fmt { get; set; }
    public string auto_bkl_off { get; set; }
    public string auto_pwr_off { get; set; }
    public string light_freq { get; set; }
    public string meter_mode { get; set; }
    public string buzzer { get; set; }
}

public class RootObject
{
    public int rval { get; set; }
    public int msg_id { get; set; }
    public Param param { get; set; }
}

Prototype fiddle.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • Thank you very much sir! Now param is deserialized as only one Param! The problem I have seen is that with this custom deserialization rval and msg_id are set to zero. I will have a look at it – kbronctjr Jan 28 '16 at 19:02
  • I can't find the way to deserialize correctly "rval" and "msg_id" Can you give me a hand? I don't need to Re-serialize it again because is a message that is only received. Thanks in advance – kbronctjr Jan 28 '16 at 20:14
  • Thanks! I finally found the problem. In my code "RootObject" is "CamSettingsMessage". This class inherits from another class called "CamMessage" that has the properties "rval" and "msg_id". The problem was that in this class i had only created a public getter, but not a setter. I guess the "Populate" method from the serializer was trying to set this properties which didn't have a setter – kbronctjr Jan 28 '16 at 22:09
-1

Let me see if I understood every thing, when you generate your C# classes you will recive something like that:

public class Param
{
    public string camera_clock { get; set; }
    public string video_standard { get; set; }
    public string app_status { get; set; }
    public string stream_out_type { get; set; }
    public string save_low_resolution_clip { get; set; }
    public string video_resolution { get; set; }
    public string video_stamp { get; set; }
    public string video_quality { get; set; }
    public string timelapse_video { get; set; }
    public string photo_size { get; set; }
    public string photo_stamp { get; set; }
    public string photo_quality { get; set; }
    public string timelapse_photo { get; set; }
    public string selfie_photo { get; set; }
    public string burst_photo { get; set; }
    public string autoshoot_photo { get; set; }
    public string loop_record { get; set; }
    public string motion_detec_video { get; set; }
    public string status_led_switch { get; set; }
    public string wifi_led_switch { get; set; }
    public string osd_switch { get; set; }
    public string cardvr_switch { get; set; }
    public string delay_pwroff { get; set; }
    public string rotate_image { get; set; }
    public string mic_vol { get; set; }
    public string language { get; set; }
    public string date_disp_fmt { get; set; }
    public string auto_bkl_off { get; set; }
    public string auto_pwr_off { get; set; }
    public string light_freq { get; set; }
    public string meter_mode { get; set; }
    public string buzzer { get; set; }
}

public class myClass
{
    public int rval { get; set; }
    public int msg_id { get; set; }
    public List<Param> param { get; set; }
}

After you will use NewtonSoft to deserialize?

you have to do somthing like

myClass deserializedProduct = JsonConvert.DeserializeObject<myClass>(output);

is that you want ?

Hope this help

우두머리
  • 545
  • 2
  • 18
  • The problem is that I get a List of Param, with all the parameters inside each of the Param set to null except for one – kbronctjr Jan 28 '16 at 18:24
  • Thanks for your answer but Yeah, that is how I have it and how I deserialize it. What I want is a single Param object. Why would I want 32 Param objects with all parameters null except one? It seems i need to create a custom deserializer for this json... – kbronctjr Jan 28 '16 at 18:26
  • When I responded to the question you were not corrected your question, so misunderstood, my bad, yeh you will have but to do it. :) – 우두머리 Jan 28 '16 at 18:36