Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bind a given property when deserializing a JSON object in c# Web Api?

I have a c# class which looks like

public class Node {

    public int Id { get; set; }

    /** Properties omitted for sake of brevity **/

    public Node ParentNode { get; set; }

}

From a browser, I submitted a JSON object as follows

{"Id":1, "ParentNode":1}

Where the value 1 assigned to the ParentNode property representes a database identifier. So, in order to bind properly to my model, i need to write a custom JSON converter

public class NodeJsonConverter : JsonConverter
{

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

        /** Load JSON from stream **/
        JObject jObject = JObject.Load(reader);

        Node node = new Node();

        /** Populate object properties **/
        serializer.Populate(jObject.CreateReader(), node);

        return node;
    }
}

Because i get a "Current JsonReader item is not an object: Integer. Path ParentNode'", how can adapt the ReadJson method in order to bind the ParentNode property or anything else which needs custom conversion ?

UPDATE

I have seen JsonPropertyAttribute whose API documentation states

Instructs the JsonSerializer to always serialize the member with the specified name

However, how can i instruct programatically a JsonSerializer - in my case, in the ReadJson method - to use a given JsonPropertyAttribute ?

http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonPropertyAttribute.htm

like image 230
Arthur Ronald Avatar asked Oct 31 '22 04:10

Arthur Ronald


1 Answers

I think the issue here is that the parsing becomes recursive due to a Node containing a Node in the form of the ParentNode property.

On the call to serializer.Populate(jObject.CreateReader(), node); the serializer will hit the ParentNode property which is of type Node and it will then attempt to parse that using your NodeJsonConverter. At that point the reader has moved on and you no longer have a StartObject but insted you have an Integer. I think you can check the reader.TokenType property to see if you are in the first call or the subsequent call and handle it accordingly:

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

    Node node = new Node();

    if (reader.TokenType == JsonToken.StartObject)
    {
        //initial call
        //here we have the object so we can use Populate
        JObject jObject = JObject.Load(reader);
        serializer.Populate(jObject.CreateReader(), node);
    }
    else
    {
        //the subsequent call
        //here we just have the int which is the ParentNode from the request
        //we can assign that to the Id here as this node will be set as the 
        //ParentNode on the original node from the first call
        node.Id = (int)(long)reader.Value;
    }

    return node;
}
like image 120
petelids Avatar answered Nov 08 '22 03:11

petelids