我正在上课,如下所示:
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; }
}
使用以下webmethod的web服务:
[WebMethod]
public String Test(MainTest input)
{
String rtrn = String.Empty;
foreach (Test test in input.Components)
rtrn += test.Name;
return rtrn;
}
AJAX使用以下方法调用它:
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);
}
});
执行AJAX调用时,它将返回错误。我发现错误是类型为TestCol
的类,它没有属性。
现在我找到了2个需要更改C#类的解决方案:
删除TestCol
类并将Components
属性更改为List<Test>
数据类型:
[DataContract()]
public class MainTest
{
public List<Test> Components { get; set; }
}
[DataContract()]
public class Test
{
public Test() { }
public String Name { get; set; }
}
或者向TestCol
类添加一个额外的属性并更改web方法:
[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;
}
两种解决方案都需要更改C#类,我不愿意这样做,因为其他代码依赖于它。 有没有人知道这个问题的解决方案?
编辑:我上传了一个测试解决方案,其中包含以上代码:http://jeroenvanwarmerdam.nl/content/temp/JSONtoClassWebservice.zip
答案 0 :(得分:1)
因此,此解决方案将List更改为Object而不是Test。我希望改变尽可能少的代码(我不喜欢在foreach循环中进行强制转换)。下面的代码通过两个函数添加和前面提到的继承更改来实现。
public class TestCol : List<object>
{
public new IEnumerator<Test> GetEnumerator()
{
return this.ConvertAll<Test>(
dict => ConvertDictionaryTo<Test>(
(Dictionary<string, object>) dict
)
).GetEnumerator();
}
private T ConvertDictionaryTo<T>(IDictionary<string, object> dictionary) where T : new()
{
Type type = typeof(T);
T ret = new T();
foreach (var keyValue in dictionary)
{
type.GetProperty(keyValue.Key).SetValue(ret, keyValue.Value, null);
}
return ret;
}
}
转换功能礼貌TurBas Mapping object to dictionary and vice versa
答案 1 :(得分:0)
嗯,这在网络方法中不起作用?
foreach (Test test in input.Components.TestCol)
在下面评论,这是否有效?
foreach (Test test in (List<Test>)input.Components.TestCol)
它应该可以工作,因为可以枚举一个类......
答案 2 :(得分:0)
如果您期待JSON,则需要返回JSON。 检查System.Web.Script.Serialization.JavaScriptSerializer http://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.aspx
答案 3 :(得分:0)
如果您使用ASMX服务,JavaScriptSerializer将负责数据转换,而不是DataContractJsonSerializer。因此,您使用的所有DataContract
属性都无效。
您写的是public class TestCol : List<Test> { }
等类对JavaScriptSerializer不利,但List<Test>
作为属性(public class MainTest { public List<Test> Components { get; set; }}
)的类没有问题。
所以我建议将代码简化为以下内容。用作参数的类可以定义为
public class Test {
public String Name { get; set; }
}
public class MainTest {
public List<Test> Components { get; set; }
}
WebMethod Test
将
[WebMethod]
public String Test(MainTest input)
{
StringBuilder rtrn = new StringBuilder();
foreach (Test test in input.Components) {
rtrn.AppendLine (test.Name);
}
return rtrn.ToString ();
}
和ajax调用可以是
var tests = {
Components: [
{Name:"Test1"},
{Name:"Test2"},
{Name:"Test3"}
]
};
$.ajax({
type: "POST",
url: "WebService1.asmx/Test",
contentType: "application/json; charset=utf-8",
dataType: "json",
data: JSON.stringify({
"input": tests
}),
success: function (data, textStatus) {
alert("success:\n" + data.d);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText+" || "+textStatus+" || "+errorThrown);
}
});
如何看待所有内容将非常简单并且有效。有关如何发送复杂数据的详细信息,建议您阅读another answer和this。
答案 4 :(得分:0)
您似乎在使用ASMX(而不是WCF),因为您在所有公共属性上省略了[DataMember]
属性,但仍然被序列化。 WCF是“选择加入”,所以你不应该看到任何正确的序列化。
因此,所有[DataContract]
属性都无用。
如果您使用ScriptManger并输出JSON,ASMX默认使用JavaScriptSerializer。 JavaScriptSerializer是“选择退出”(这意味着所有公共属性都会自动序列化,除非标有[ScriptIgnoreAttribute]
)。
JavaScriptSerializer支持序列化List&lt;&gt ;.您不应该在序列化TestCol属性时遇到问题,因为JavaScriptSerializer自动支持序列化实现IEnumerable(但不是IDictionary)的所有类型 - 其中包括List&lt;&gt; - 进入JSON数组。
您的错误似乎是JavaScriptSerializer无法正确处理来自List&lt;&gt;的继承的类(或从实现IEnumerable的类)。在第一次解决方法中,您删除了从List&lt;&gt;继承的类。在第二种解决方法中,您跳过了基类的所有功能,但重新实现了List&lt;&gt;在一个财产。
您的JSON帖子数据目前如下:
{ Components: [
{ Name:"foo" },
{ Name:"bar" },
:
] }
但是,序列化程序中有一个额外级别或重定向(继承自List<Test>
- &gt; TestCol
)。序列化程序可能正在寻找:
{ Components: {
Items: [
{ Name:"foo" },
{ Name:"bar" },
:
] }
}
因为您基本上是序列化List&lt;&gt;的“Items”属性。因此,您的JSON帖子数据只是将Test对象送到错误的位置,而您的TestCol Components属性最终为空。
我建议您添加一个Web服务方法来输出测试MainTest对象以查看它序列化的内容。您可能会发现它会增加一个级别。
答案 5 :(得分:0)
当使用JavaScriptSerializer时,它会自动转换IEnumerable(不带IDictionary)类型 - 它涵盖List&lt;&gt;或者从它衍生的任何东西 - 变成一个数组。
现在,在JSON的反序列化之后,JavaScriptSerializer必须获取数组,创建一个IEnumerable,然后通过将该IEnumerable传递给它的构造函数来为该字段创建一个对象。
现在,对于List&lt;&gt;你有一个构造函数重载,它采用IEnumerable。因此,如果您将List<Test>
作为组件的类型,则可以创建它。
但是,TestCol NOT 有这样的构造函数!它与List<Test>
而非TestCol
(源自List<Test>
)的原因是,在类之间继承的唯一不是构造函数!
因此,JavaScriptSerializer没有任何方法可以从IEnumerable构造TestCol。所以它无声地失败了。
现在,JavaScriptSerializer可能会尝试从此List<Test>
创建IEnumerable<Test>
,然后尝试将其转换为TestCol。
解决方案:尝试投入:
public TestCol () {} // Need this when you have another constructor
public TestCol (IEnumerable<Test> list) : base(list) {} // Constructor that takes an IEnumerable
public TestCol (IList<Test> list) : base(list) {} // Constructor that takes an IList
作为TestCol的构造函数。
如果它仍然不起作用,请实施从List<Test>
到TestCol
的显式类型转换。
public static explicit operator TestCol(IList<Test> list) { return new TestCol(list); }