Create a list of interface types from a JavaScript array

Let's say that there is a simple interface:

public interface ISerialize { string FirstProp { get; set; } string SecondProp { get; set; } } 

which is implemented by the classes:

  public class Class1 : ISerialize { public string FirstProp { get; set; } public string SecondProp { get; set; } public string ThirdProp { get; set; } } public class Class2 : ISerialize { public string FirstProp { get; set; } public string SecondProp { get; set; } public string FourthProp { get; set; } } 

at the moment (which is not intended for long-term stability) I have a web page that looks like this: http://jsfiddle.net/SBbPT/ , where each text field corresponds to a property of a Class1 or Class2 object, and the Add to batch link adds object to the JavaScript array and the Submit batch button sends the JSON string to the string object web service. Currently, the following JS determines which type is created by Class1 or Class2 :

  $(document).ready(function () { var iSerialize = []; $('#btnSubmit').click(function () { //creates Class1 object if ThirdProp is present if ($('#txt3').val().length > 0) { var class1 = { FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), ThirdProp: $('#txt3').val() } iSerialize.push(class1); } else { var class2 = { FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), FourthProp: $('#txt4').val() }; iSerialize.push(class2); } $('input').val(''); }); $('#btnSubmitBatch').click(function () { var data = "{jsonString:'" + JSON.stringify(iSerialize) + "'}"; console.log(data); $.ajax( { type: "POST", url: "default.aspx/DataList", contentType: "application/json", dataType: "json", data: data, success: function (data) { console.log('the post was successful'); console.log(data.d); }, error: function (xhr) { console.log(xhr.status); } }); }); }); 

Currently, if the user leaves the FourthProp text field empty, a Class1 object must be created, and if the user leaves the ThirdProp text field empty, a Class2 object must be created. My current web service method is as follows:

 [WebMethod] public string DataList(string jsonString) { var jss = new JavaScriptSerializer(); List<ISerialize> list = jss.Deserialize<List<ISerialize>>(jsonString); //some members might have different properties //how to create a factory to create an instance based on the objects properties? return list[0].FirstProp; } 

In the current state, I get the error message: No parameterless constructor defined for type of DeserializeListOfInterfaceTypes.ISerialize. This can be avoided, and the program will work by making the List<ISerialize> list of one of the specific types. Therefore, in this case, the presence of the ThirdProp or FourthProp determines whether the object should be Class1 or Class2 , respectively. How can I use the properties of a JavaScript object to determine which C # object to create?

+4
source share
4 answers

If you want to continue using the JavaScriptSerializer , you can write custom JavaScriptTypeResolver :

 public class CustomTypeResolver : JavaScriptTypeResolver { public override Type ResolveType(string id) { return id == "class1" ? typeof(Class1) : typeof(Class2); } public override string ResolveTypeId(Type type) { return type == typeof(Class1) ? "class1" : "class2"; } } 

and then create a serializer as follows:

 var jss = new JavaScriptSerializer(new CustomTypeResolver()); 

Finally, you need to add this type of information to the __type field in your JavaScript code:

 $('#btnSubmit').click(function () { //creates Class1 object if ThirdProp is present if ($('#txt3').val().length > 0) { var class1 = { __type: "class1", FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), ThirdProp: $('#txt3').val() } iSerialize.push(class1); } else { var class2 = { __type: "class2", FirstProp: $('#txt1').val(), SecondProp: $('#txt2').val(), FourthProp: $('#txt4').val() }; iSerialize.push(class2); } $('input').val(''); }); 

However, if you do not want to add any other properties to your objects, I would agree with jods answer and use Newtonsoft JSON.NET instead of JavaScriptSerializer .

+1
source

The problem is that the de-serializer needs to instantiate classes to populate the list, not the interfaces. But he does not know which class.

So your question comes down to: how to hint to the deserializer which class to create for each JSON object?

There is no standard way to include a class definition in JSON (at least for now). You should come up with your own rule (include a type property or check existing properties as you suggest). Of course, the JSON structure cannot know the rules, so this will not happen “magically”.

Assuming you are using Newtonsoft JSON.net for serialization (unless you do). This exact question was asked and answered here:

How to implement custom JsonConverter in JSON.NET to deserialize a list of base class objects?

It basically comes down to overriding JsonConverter .

+2
source

You can solve this in two ways:

  • Write your own serializer that will know your specific requirements and have logic about what type to instantiate.
  • Change the object model of your server, depending on the semantics of your web application.

If you take approach 1, instead of JavaScriptSerializer , you must implement a custom one. Here's a starting point on how to do this: http://msdn.microsoft.com/en-us/library/ty01x675.aspx

If you decide to change your object model, you need to make sure that it works with the default JavaScriptSerializer , which requires the creation of specific types. Now, depending on the semantics, Class1 and Class2 can be similar to each other (inheritance). In this case, you can extract the properties that are currently included in the interface into the base class and do whatever needs to be redefined virtual. Then Class1 and Class2 override the properties, but they will still be defined in the base class, and the serializer will be able to instantiate this class. Instead of an interface, you must specify the base class as a type of serializer. If Class1 and Class2 do not use any behavior, you can still create a base class for servicing only for serialization. Of course, it depends on the semantics of which I know nothing.

+2
source

The path that I personally chose may be as follows:

Create a class that can accommodate any given input for Class1 plus Class2 , e.g.

 public class ClassInput { public string FirstProp { get; set; } public string SecondProp { get; set; } public string ThirdProp { get; set; } public string FourthProp { get; set; } } 

Use this class as an argument to your web method.

Then determine on the server side which class should be created.

There is a method for each of Class1 and Class2 that creates this class and displays each property from ClassInput .

 public Class1 CreateClass1(ClassInput input) { Class1 output = new Class1(); output.FirstProp = input.FirstProp; output.SecondProp = input.SecondProp; output.ThirdProp = input.ThirdProp; return output; } public Class2 CreateClass2(ClassInput input) { Class2 output = new Class1(); output.FirstProp = input.FirstProp; output.SecondProp = input.SecondProp; output.FourthProp = input.FourthProp; return output; } 

Using AutoMapper , you can save some code here if you have many classes / properties.

+1
source

Source: https://habr.com/ru/post/1501532/


All Articles