I’m having a class like the following:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
[DataContract()]
public class TestCol : List<Test> { }
[DataContract()]
public class MainTest
{
public TestCol Components { get; set; }
}
[DataContract()]
public class Test
{
public Test() { }
public String Name { get; set; }
}
And a webservice with the following webmethod like this:
[WebMethod]
public String Test(MainTest input)
{
String rtrn = String.Empty;
foreach (Test test in input.Components)
rtrn += test.Name;
return rtrn;
}
Which is called by AJAX with the following method:
var Test = {};
Test.Name = "Test";
var MainTest = {};
MainTest.Components = [];
MainTest.Components.push(Test);
$.ajax({
type: "POST",
url: "WebService/WSTest.asmx/Test",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({
"input": MainTest
}),
success: function(data, textStatus) {
console.log("success");
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
window.console && console.log && console.log(XMLHttpRequest.responseText + " || " + textStatus + " || " + errorThrown);
}
});
When executing the AJAX call, it will return errors. I found out that the error is with the typed class TestCol, which has no properties.
Now do I have found 2 solutions that require changes in the C# classes:
-
Remove the
TestColclass and change theComponentsproperty toList<Test>datatype:[DataContract()] public class MainTest { public List<Test> Components { get; set; } } [DataContract()] public class Test { public Test() { } public String Name { get; set; } } -
Or add an extra property to the
TestColclass and change the webmethod:[DataContract()] public class TestCol : List<Test> { public List<Test> Components { get; set; } } [DataContract()] public class MainTest { public TestCol Components { get; set; } } [DataContract()] public class Test { public Test() { } public String Name { get; set; } }&
[WebMethod] public String Test(MainTest input) { String rtrn = String.Empty; foreach (Test test in input.Components.Components) rtrn += test.Name; return rtrn; }
Both solutions require changes in the C# classes, which I prefer not to, as other code is depended on it. Does anyone know a solution for this problem?
Edit: I’ve uploaded a test solution, containing above code: http://jeroenvanwarmerdam.nl/content/temp/JSONtoClassWebservice.zip
JavaScriptSerializer serialization: IEnumerable -> JavaScript Array
When the JavaScriptSerializer is used, it automatically converts an IEnumerable (without IDictionary) type — that covers List<> or anything derived from it — into an array.
Deserialization: JavaScript Array -> IEnumerable -> Collection Object
Now, upon deserialization from JSON, the JavaScriptSerializer must take the array, create an IEnumerable, then create an object for the field by passing that IEnumerable into its constructor.
Constructing Collection object via Constructor
Now, for List<> you have a constructor overload that takes an IEnumerable. So if you put
List<Test>as the type of your component it creates it fine.Constructors not inherited
However, TestCol does NOT have such a constructor! The reason why it worked with
List<Test>and not withTestCol(which derives fromList<Test>) is that the only thing that is not inherited between classes are constructors!Therefore, the JavaScriptSerializer does not have any way to construct a TestCol from an IEnumerable. So it fails silently.
Deserialize Array by Creating List, then Casting to Type
Now the JavaScriptSerializer may then attempt to create a
List<Test>from thisIEnumerable<Test>, and then try to cast it into a TestCol.Possible Solution
Solution: Try putting in:
as your TestCol’s constructors.
And if it still doesn’t work, implement an explicit type cast from
List<Test>toTestCol.