简体   繁体   中英

c# JavaScriptConverter - how to deserialize custom property?

I've got a class which has been serialized into JSON, and which I'm trying to deserialize into an object.

eg

public class ContentItemViewModel
{
    public string CssClass { get; set; }
    public MyCustomClass PropertyB { get; set; }
}

the simple property (CssClass) will deserialize with:

 var contentItemViewModels = ser.Deserialize<ContentItemViewModel>(contentItems);

But PropertyB gets an error...

We added a JavaScriptConverter:

  ser.RegisterConverters(new List<JavaScriptConverter>{ publishedStatusResolver});

But when we added 'MyCustomClass' as a 'SupportedType', the Deserialize method was never called. However when we have ContentItemViewModel as the SupportedType, then Deserialize is called.

We've got a current solution which looks something like this:

class ContentItemViewModelConverter : JavaScriptConverter
{

    public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
    {
        var cssClass =  GetString(dictionary, "cssClass"); //I'm ommitting the GetString method in this example...           
        var propertyB= GetString(dictionary, "propertyB");

        return new ContentItemViewModel{    CssClass = cssClass , 
                                            PropertyB = new MyCustomClass(propertyB)}
    }

    public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
    {
        throw new Exception("Only does the Deserialize");
    }

    public override IEnumerable<Type> SupportedTypes
    {
        get
        {
            return new List<Type>
                {
                    typeof(ContentItemViewModel)
                };
        }
    }
}

But we'd prefer a simpler solution of only deserializing MyCustomClass, as there are a number of other fields which are on the ViewModel, and it seems a waste to have to edit this converter every time we change/add a property....

Is there a way to Deserialize JUST PropertyB of type MyCustomClass?

Thanks for your help!

For whatever it may be worth after all this time, but I stumbled over the same problem and the solution is that the Deserializer hasn't got a clue about the classes you are deserializing unless you give him the necessary information.

On the top level, it knows the type from the type parameter of Deserialize<>(). That's why your converter for ContentItemViewModel works. For nested objects, it needs __type properties and a JavaScriptTypeResolver.

var ser = new JavaScriptSerializer(new SimpleTypeResolver());
ser.RegisterConverters(myconverters);
MyClass myObject = new MyClass();
string json = ser.Serialize(myObject);
    // set a breakpoint here to see what has happened
ser.Deserialize<MyClass>(json);

A TypeResolver adds a __type property to each serialized object. You can write a custom type resolver that uses short names. In this sample, I use the SimpleTypeResolver from .net that "simply" stores the fully qualified type name as __type. When deserializing, the JavaScriptDeserializer finds __type and asks the TypeResolver for the correct type. Then it knows a type and can call a registered JavaScriptConverter.Deserialize method.

Without a TypeResolver, objects are deserialized to a Dictionary because JavaScriptSerializer doesn't have any type information.

If you can't provide a __type property in your json string, I think you'll need to deserialize to Dictionary first and then add a "guessing-step" that interprets the fields to find the right type. Then, you can use the ConvertToType method of JavaScriptSerializer to copy the dictionary into the object's fields and properties.

If you need to use the JavaScriptSerializer that is provides by ASP.NET and can't create your own, consider this section from the .ctor help of JavaScriptSerializer:

The instance of JavaScriptSerializer that is used by the asynchronous communication layer for invoking Web services from client script uses a special type resolver. This type resolver restricts the types that can be deserialized to those defined in the Web service's method signature, or the ones that have the GenerateScriptTypeAttribute applied. You cannot modify this built-in type resolver programmatically.

Perhaps the GenerateScriptType Attribute can help you. But I don't know what kind of __type Properties are be needed here.

Have you considered using DatacontractJsonSerializer

[DataContract]
public class MyCustomClass
{
    [DataMember]
    public string foobar { get; set; }
}

[DataContract]
public class ContentItemViewModel
{
    [DataMember]
    public string CssClass { get; set; }
    [DataMember]
    public MyCustomClass PropertyB { get; set; }
}

class Program
{
    static void Main(string[] args)
    {

        ContentItemViewModel model = new ContentItemViewModel();
        model.CssClass = "StackOver";
        model.PropertyB = new MyCustomClass();
        model.PropertyB.foobar = "Flow";

        //Create a stream to serialize the object to.
        MemoryStream ms = new MemoryStream();

        // Serializer the User object to the stream.
        DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(ContentItemViewModel));
        ser.WriteObject(ms, model);
        byte[] json = ms.ToArray();
        ms.Close();
        string s=  Encoding.UTF8.GetString(json, 0, json.Length);


        Console.ReadLine();
    }
}

Add all possible classes to DatacontractJsonSerializer.KnownTypes if MyCustomClass has derivations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM