Search code examples
c#.netjsonjavascriptserializer

Deserializing inner structure to string with JavaScriptSerializer in .NET


I am using the JavaScriptSerializer in .NET to deserialize structures which can be your standard flat key/value pair like this:

{
    "Value": 3.14,
    "Type": "Real"
}

Or more complex, with an inner hierarchical value object like this:

{
    "Value": { "something": "test" },
    "Type": "JsonData"
}

The problem I have is that in the case of "Type": "JsonData", the object can be arbitrary. I can't make any assumption as to what its structure will be.

However, the "upper layers" of my application will know what to do with this data.

I thus need to figure out a way to keep the Value field a string, no matter if it contains an integer, a string, or a complete JSON structure. Basically, I don't want the JavaScriptSerializer to parse anything in there. I would love to be able to have such a class:

public class JsonObject
{
    public string Value { get; set; }
    public string Type { get; set; }
}

With Value containing the unprocessed, vanilla value that was in my JSON.

Is there any way to tell .NET to NOT to parse some part of my JSON?

Please do not suggest using another library like JSON.NET. I know it would probably be capable of doing this, but this is for a project which will be distributed as a library -- This is why I'd rather have my library depend only on the standard .NET framework.


Solution

  • There isn't an attribute to instruct the JavaScriptSerializer not to parse a particular JSON property (other than to completely ignore it, which is not what you want). However, you could make a JavaScriptConverter which would effectively re-serialize the property in question such that you still get the result you want: all the values end up as strings.

    Here is what the converter might look like:

    public class JsonObjectConverter : JavaScriptConverter
    {
        public override IEnumerable<Type> SupportedTypes
        {
            get { return new List<Type> { typeof(JsonObject) }; }
        }
    
        public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
        {
            JsonObject obj = new JsonObject();
            obj.Type = (string)dictionary["Type"];
            obj.Value = serializer.Serialize(dictionary["Value"]);
            return obj;
        }
    
        public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    To use the converter, you must register it with the JavaScriptSerializer using the RegisterConverters method. Here is a demo:

    class Program
    {
        static void Main(string[] args)
        {
            string json = @"
            [
                {
                    ""Value"" : ""foo bar"",
                    ""Type"" : ""String""
                },
                {
                    ""Value"" : 3.14,
                    ""Type"" : ""Real""
                },
                {
                    ""Value"" : 
                    {
                        ""Something"" : ""Test"",
                        ""Items"" : [""A"", ""B"", ""C""]
                    },
                    ""Type"" : ""JsonData""
                },
                {
                    ""Value"" : 42,
                    ""Type"" : ""Integer""
                }
            ]";
    
            JavaScriptSerializer jss = new JavaScriptSerializer();
            jss.RegisterConverters(new List<JavaScriptConverter> { new JsonObjectConverter() });
    
            List<JsonObject> list = jss.Deserialize<List<JsonObject>>(json);
            foreach (JsonObject item in list)
            {
                Console.WriteLine(string.Format("({0}) {1}", item.Type, item.Value));
            }
        }
    }
    
    public class JsonObject
    {
        public string Value { get; set; }
        public string Type { get; set; }
    }
    

    Output:

    (String) "foo bar"
    (Real) 3.14
    (JsonData) {"Something":"Test","Items":["A","B","C"]}
    (Integer) 42