Search code examples
c#jsonjson.net

Is it possible to leave fields as raw text when deserializing?


I have json like this

{
    "guid": "b1e3c29f-066f-417b-b6b6-795ffae90f0a",
    "status": "complete",
    "type": "colors",    
    "results": {
        "events": [
            {
                "title": "event1",
                "other": {
                    "Red": "red",
                    "Green": "green",
                    "Blue": "blue"
                }
            },
            {
                "title": "event2",
                "other": {
                    "Yelow": "yellow",
                    "Orange": "orange"
                }
            }
        ]
    }
}

and classes for deserialization looks like this

public class Other
{
    public string Red { get; set; }
    public string Green { get; set; }
    public string Blue { get; set; }
    public string Yelow { get; set; }
    public string Orange { get; set; }
}

public class Event
{
    public string title { get; set; }
    public Other other { get; set; }
}

public class Results
{
    public List<Event> events { get; set; }
}

public class Root
{
    public string guid { get; set; }
    public string status { get; set; }
    public string type { get; set; }
    public Results results { get; set; }
}

When I deserialize this json with

var root = JsonConvert.DeserializeObject<Root>(JsonContent); 

everything works fine.

The problem is that the content of json section "Other" is changeable and I don't know its complete content.
Also I need to put this classes in database.

Is there a way to deserialize json section "Other" as one string field with raw text, and to look something like this:

public class Other
{
    public string AllFieldsFromOtherSectionAsRawText { get; set; }
}

Solution

  • If you define your Other class like this:

    public class Other
    {
        public Other(string rawData)
        {
            Content = rawData;
        }
        public string Content { get; }
    }
    

    Then you can write a custom JsonConverter, which will treat your other nodes as string:

    class ObjectToStringJsonConverter : JsonConverter
    {
        private readonly Type theType;
        public ObjectToStringJsonConverter(Type type) => theType = type;
    
        public override bool CanConvert(Type objectType) => objectType == theType;
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            => throw new NotImplementedException();
    
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken token = JToken.Load(reader);
            if (token.Type == JTokenType.Object)
            {
                return Activator.CreateInstance(theType, args: token.ToString()); 
            }
            throw new NotSupportedException("The related node is not object");
        }
    }
    

    The only trick here is this line:

    • Activator.CreateInstance(theType, args: token.ToString());
    • Here we create a new Other instance and pass the string representation of the other node to the constructor.

    Then the usage would look like this:

    var root = JsonConvert.DeserializeObject<Root>(json, new[] { new ObjectToStringJsonConverter(typeof(Other)) });