DirtyNative
DirtyNative

Reputation: 2844

JSon.Net deserialize custom class

I want to deserialize a json object to a custom class. The class could look like this:

public class CommunicationMessage {

    public string Key { get; set; }

    public string Value { get; set; }

    public List<CommunicationMessage> Childs { get; set; }
}

And the json I want to deserialize looks like this:

{
"Skills": [{
    "Skill": [{
        "SkillID": "1",
        "ParticipantID": "7",
        "CanDo": "True"
    }, {
        "SkillID": "2",
        "ParticipantID": "7",
        "CanDo": "True"
    }, {
        "SkillID": "3",
        "ParticipantID": "7",
        "CanDo": "False"
    }]
}]
}

And this is the code I am using to deserialize the json:

private void ReadRecursive(JToken token, ref CommunicationMessage root) {

        if (token is JProperty) {

            CommunicationMessage msg = new CommunicationMessage();

            if (token.First is JValue) {
                msg.Key = ((JProperty)token).Name;
                msg.Value = (string)((JProperty)token).Value;
            } else {

                msg.Key = ((JProperty)token).Name;

                foreach (JToken child in token.Children()) {
                    ReadRecursive(child, ref msg);
                }
            }
            root.Childs.Add(msg);
        } else {
            foreach (JToken child in token.Children()) {
                ReadRecursive(child, ref root);
            }
        }
    }

I am expecting to get this hirarchy:

Skills
    Skill
         SkillID:1
         ParticipantID:7
         CanDo:true
    Skill
         SkillID:2
         ParticipantID:7
         CanDo:true
    Skill
         SkillID:3
         ParticipantID:7
         CanDo:false

But I am getting this:

Skills
    Skill
         SkillID:1
         ParticipantID:7
         CanDo:
         SkillID:2
         ParticipantID:7
         CanDo:true
         SkillID:3
         ParticipantID:7
         CanDo:false

I can't find the lines where my failure is, so maybe anyone can help me here.

Thanks!!

Upvotes: 0

Views: 230

Answers (4)

blins
blins

Reputation: 2535

You could just check for when you are at the right level/object type using logic inside your recursive method.

void ReadRecursive(JToken token, ref CommunicationMessage root)
{
    var p = token as JProperty;

    if (p != null && p.Name == "Skill")
    {
        foreach (JArray child in p.Children())
        {
            foreach (JObject skill in child.Children())
            {
                // Create/add a Skill message instance for current Skill (JObject)
                var skillMsg = new CommunicationMessage { Key = p.Name };

                // Populate Childs for current skill instance
                skillMsg.Childs = new List<CommunicationMessage>();
                foreach (JProperty skillProp in skill.Children())
                {
                    skillMsg.Childs.Add(new CommunicationMessage
                    {
                        Key = skillProp.Name,
                        Value = (string)skillProp.Value
                    });
                }

                root.Childs.Add(skillMsg);
            }
        }
    }

    // Recurse
    foreach (JToken child in token.Children())
        ReadRecursive(child, ref root);
}

Upvotes: 0

Exponent
Exponent

Reputation: 94

Using a DataContractJsonSerializerfrom System.Runtime.Serializationwould make the deserialization easier:

Stream data = File.OpenRead(@"data.json");
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(CommunicationMessage));
CommunicationMessage message = (CommunicationMessage)serializer.ReadObject(data);

But you also need a class like this:

[DataContract]
class CommunicationMessage
{
    [DataContract]
    class SkillsData
    {
        [DataContract]
        internal class SkillData
        {
            [DataMember(Name = "SkillID")]
            internal object SkillID;
            [DataMember(Name = "ParticipantID")]
            internal object ParticipantID;
            [DataMember(Name = "CanDo")]
            internal object CanDo;
        }


        [DataMember(Name = "Skill")]
        internal SkillData[] Skill;
    }


    [DataMember(Name = "Skills")]
    SkillsData[] Skills;
}

Above you have the class SkillData, which holds the data of each skill. So if you take the array Skill, you have the wanted hirarchy.

Upvotes: 0

Scott Hannen
Scott Hannen

Reputation: 29282

Use Newtonsoft Json.NET.

output message = JsonConvert.DeserializeObject<CommunicationMessage>(json);

(where json is the JSON string.)

I used this page - json2csharp - to create classes that match the JSON you posted:

public class Skill2
{
    public string SkillID { get; set; }
    public string ParticipantID { get; set; }
    public string CanDo { get; set; }
}

public class Skill
{
    public List<Skill2> Skill { get; set; }
}

public class CommunicationMessage
{
    public List<Skill> Skills { get; set; }
}

The class names are autogenerated. It always names the root object RootObject. But you can change it to CommunicationMessage (I did.)

If you want the class to have different property names that don't match the JSON you can do that with attributes.

public class Skill2
{
    [JsonProperty["Key"]
    public string SkillID { get; set; }
    [JsonProperty["Value"]
    public string ParticipantID { get; set; }
    public string CanDo { get; set; }
}

Upvotes: 0

Jan K&#246;hler
Jan K&#246;hler

Reputation: 6030

Your code seems to do its job quite ok (although there are simpler ways to achieve your goal). The problematic part is the JSON it self. It's organized in two arrays.

So your code puts out the Skills-array (which has one element) and the Skill-array which holds the actual the 3 skills you're expecting.

{
    "Skills": [{        // array -> note the [
        "Skill": [{     // array -> note the [

Hence one way to solve this would be to edit the JSON (if this is possible):

{
    "Skills": [{
        "SkillID": "1",
        "ParticipantID": "7",
        "CanDo": "True"
    }, {
        "SkillID": "2",
        "ParticipantID": "7",
        "CanDo": "True"
    }, {
        "SkillID": "3",
        "ParticipantID": "7",
        "CanDo": "False"
    }]
}

Upvotes: 1

Related Questions