0

I have the following C# object:

public abstract partial class ClientTreeNode {
    public int ID { get; internal set; }
    public string Question { get; internal set; }
    public List<ClientTreeNode> Children { get; internal set; }
    public QuestionCategories Category { get; internal set; }
    public Dictionary<object, List<int>> AnswerNodes { get; internal set; }

    public string Type => GetType().Name.Replace("TreeNode", "").FirstCharacterToLower();

    public string CategoryText {
        get {
            switch (Category) {
                case QuestionCategories.VisualInspection:
                    return "Sichtprüfung";
                case QuestionCategories.MechanicalInspection:
                    return "Mechanische Prüfung";
                case QuestionCategories.ElectricalInspection:
                    return "Elektrische Prüfung";
                default:
                    return "Fehler";
            }
        }
    }

    public abstract AnswerResult Answer(JObject data);

    internal static ClientTreeNode FromDatabase(int ID, out int TotalChildNodes) {
        // ....
    }

    internal static int SumUpChildNodes(List<int> nodeIDs) {
        using (var db = new DatabaseEntities()) {
            return db.TreeNodes
                .Where(tn => nodeIDs.Contains(tn.ID))
                .Sum(tn => tn.TotalChildNodes);
        }
    }

    [JsonConverter(typeof(StringEnumConverter), true)]
    public enum QuestionCategories {
        VisualInspection,
        MechanicalInspection,
        ElectricalInspection
    }
}

public class YesNoTreeNode : ClientTreeNode {
    public bool Result { get; internal set; }

    public override AnswerResult Answer(JObject data) {
        if (data["result"].Type != JTokenType.Boolean)
            throw new ArgumentException("The provided answer was invalid.");

        Result = data["result"].Value<bool>();
        Children = new List<ClientTreeNode>();

        foreach (var childNodeID in AnswerNodes[Result])
            Children.Add(FromDatabase(childNodeID, out _));

        return new AnswerResult(SumUpChildNodes(AnswerNodes[!Result]), Children);
    }
}

The JSON file looks like this:

{"AnswerNodes":{"True":[4],"False":[5]}}

Sometimes, it can be a little bit more advanced, like this. But it's not mandatory in all cases:

{"Result":false,"ID":0,"Question":null,"Children":null,"Category":"visualInspection","AnswerNodes":{"True":[4],"False":[5]},"Type":"yesNo","CategoryText":"Sichtprüfung"}

When I try to decode that using the following code, all values are getting filled, except for the AnswerNodes dictionary. This always becomes null:

JsonConvert.DeserializeObject<YesNoTreeNode>(node.NodeOptionsJSON);

// This is a workaround:
if (ret.AnswerNodes is null)
    ret.AnswerNodes = JObject.Parse(node.NodeOptionsJSON)["AnswerNodes"].ToObject<Dictionary<object, List<int>>>();

Even in the following test scenario, it doesn't work. Therefore, I can except, that this is due to malformed JSON code.

ret = loadFromJSON ? JsonConvert.DeserializeObject<YesNoTreeNode>(node.NodeOptionsJSON) : new YesNoTreeNode();

// At this point, ret.AnswerNodes is null

ret.AnswerNodes = new Dictionary<object, List<int>>();
ret.AnswerNodes.Add(1, new List<int>() { 4 });
ret.AnswerNodes.Add(2, new List<int>() { 5 });

var test = JsonConvert.SerializeObject(ret);
var test2 = JsonConvert.DeserializeObject<YesNoTreeNode>(test);

Is there a way to make the DeserializeObject method decode the object correctly in the first place?

7
  • 1
    You didn't post the JSON string. Most likely the contents of the AnswerNodes tag can't be treated as the type you want. JSON can only have strings as tags anyway so you should use a Dictionary<string,....> Commented Feb 6, 2019 at 9:48
  • @PanagiotisKanavos If you look at my last sample (the one with test and test2), it can't even decode the JSON that it creates itself. I'll add some sample JSON above. Commented Feb 6, 2019 at 9:53
  • 1
    Post the JSON string, don't force people to guess. I'll repeat that you can't have non-string tags, so using Dictionary<string,... is unusual Commented Feb 6, 2019 at 9:54
  • @PanagiotisKanavos I've just edited my question above. Commented Feb 6, 2019 at 9:56
  • Category is a string but your class expects a QuestionCategories. Is that an enum? Commented Feb 6, 2019 at 10:01

1 Answer 1

2

None of the properties are set because their setters are internal set;. This means they aren't visible to other assemblies, like Json.NET.

The following code in Linqpad :

public enum QuestionCategories 
{
    None,
    visualInspection
}

public abstract partial class ClientTreeNode {
    public int ID { get; internal set; }
    public string Question { get; internal set; }
    public List<ClientTreeNode> Children { get; internal set; }
    public QuestionCategories Category { get; internal set; }
    public Dictionary<object, int[]> AnswerNodes { get; internal set; }
}

public class YesNoTreeNode : ClientTreeNode {
    public bool Result { get; internal set; }

}

void Main()
{
    var json="{'Result':false,'ID':0,'Question':null,'Children':null,'Category':'visualInspection','AnswerNodes':{'True':[4],'False':[5]},'Type':'yesNo','CategoryText':'Sichtprüfung'}";
    var obj=JsonConvert.DeserializeObject<YesNoTreeNode>(json);
    JsonConvert.SerializeObject(obj).Dump();
}

Produces :

{ "Result":false,
  "ID":0,
  "Question":null,
  "Children":null,
  "Category":0,
  "AnswerNodes":null
}

Removing internal produces the expected result :

{ "Result":false,
  "ID":0,"Question":null,"Children":null,"Category":1,
  "AnswerNodes":{"True":[4],"False":[5]}
}

By default, Json.NET only sets public properties.

Internal setters and even private setters can be used if the properties are marked with the JsonProperty attribute :

public abstract partial class ClientTreeNode {

    [JsonProperty]
    public int ID { get;  private set; }

    [JsonProperty]
    public string Question { get;  private set; }

    [JsonProperty]
    public List<ClientTreeNode> Children { get;  private set; }

    [JsonProperty]
    public QuestionCategories Category { get;  private set; }

    [JsonProperty]
    public Dictionary<object, int[]> AnswerNodes { get;  private set; }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much. Adding the JsonProperty annotation did the trick for me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.