Reputation: 35
I am working on a C# application which has a browser integrated with it. The browser will send some data to C# in json format.
Some of the fields from json can be dserialized using javascript deserializer, but I have some data for which a custom deserializer is required, I need to register a deserializer for that but the thing is the custom deserializer must be called only for those special data and the default javascript deserializer must be called for other data, the special data can be identified from there target field's data type / name in C#. How can I achieve this.
something like this.
public class example
{
public string abc;
public someOtherDataType xyz;
public void example()
{
serializer = new JavaScriptSerializer();
// receive json string
serializer.RegisterConverters(new JavaScriptConverter[]
{
new System.Web.Script.Serialization.CS.CustomConverter()
});
//call deserializer
}
}
The json string will be something like
{
"abc" : "valueabc"
"xyz" : "valueXYZ"
}
Now the custom deserializer must be called only during deserializing xyz and default must be called for abc.
Thank you.
Upvotes: 2
Views: 1699
Reputation: 116544
The difficulty here is that a JavaScriptConverter
allows you to map a JSON object from and to a c# class -- but in your JSON, "xyz"
is just a string, not an object. Thus you can't specify a converter for someOtherDataType
and instead must specify converters for every class that contains an instance of someOtherDataType
.
(Note that the custom converter functionality in Json.NET does not have this restriction. If you were willing to switch to that library you could write a JsonConverter
converting all uses of someOtherDataType
from and to a JSON string.)
To write such a JavaScriptConverter
:
JavaScriptConverter.Deserialize
Dictionary<string, Object>
filtering out the fields requiring custom conversion.new JavaScriptSerializer.ConvertToType<T>
to deserialize the standard fields from the filtered dictionary.SupportedTypes
to return the container type.Thus, in your example, you could do:
public class example
{
public string abc;
public someOtherDataType xyz;
}
// Example implementation only.
public class someOtherDataType
{
public string SomeProperty { get; set; }
public static someOtherDataType CreateFromJsonObject(object xyzValue)
{
if (xyzValue is string)
{
return new someOtherDataType { SomeProperty = (string)xyzValue };
}
return null;
}
}
class exampleConverter : JavaScriptConverter
{
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(example) }; }
}
// Custom conversion code below
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
var defaultDict = dictionary.Where(pair => pair.Key != "xyz").ToDictionary(pair => pair.Key, pair => pair.Value);
var overrideDict = dictionary.Where(pair => !(pair.Key != "xyz")).ToDictionary(pair => pair.Key, pair => pair.Value);
// Use a "fresh" JavaScriptSerializer here to avoid infinite recursion.
var value = (example)new JavaScriptSerializer().ConvertToType<example>(defaultDict);
object xyzValue;
if (overrideDict.TryGetValue("xyz", out xyzValue))
{
value.xyz = someOtherDataType.CreateFromJsonObject(xyzValue);
}
return value;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
}
And then, to test:
public class TestClass
{
public static void Test()
{
// receive json string
string json = @"{
""abc"" : ""valueabc"",
""xyz"" : ""valueXYZ""
}";
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[]
{
new exampleConverter()
});
var example = serializer.Deserialize<example>(json);
Debug.Assert(example.abc == "valueabc" && example.xyz.SomeProperty == "valueXYZ"); // No assert
}
}
Upvotes: 2