ASP.Net webservices 中的 JSON 数组类型解析

Posted

技术标签:

【中文标题】ASP.Net webservices 中的 JSON 数组类型解析【英文标题】:JSON array type resolution in ASP.Net webservices 【发布时间】:2011-06-04 11:02:38 【问题描述】:

所以我已经研究出如何将我的自定义对象传递给 ASP.Net json webservices。很有魅力。

我遇到的问题是传入自定义对象的直接数组,或者传入作为自定义对象参数的数组。

比如说……

Public Class WebService1
    Inherits System.Web.Services.WebService

    <WebMethod()> _
    <ScriptMethod(ResponseFormat:=ResponseFormat.Json)> _
    Public Function AddPersonList(ByVal PersonList As PersonList) As String
        Debug.Assert(False)
    End Function

    Public Class Person
        Public Sub New()
        End Sub

        Public Property FirstName As String
        Public Property LastName As String
    End Class

    Public Class PersonList
        Inherits List(Of Person)

    End Class
End Class

<script>
        $(function () 
            $.ajax(
                type: "POST",
                url: "WebService1.asmx/AddPersonList",
                data: "  PersonList: [  FirstName: 'Max', LastName: 'Gershkovich' ,  FirstName: 'Test1', LastName: 'Test2'  ] ",
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                success: function (e)  debugger; ,
                error: function (e)  debugger; 
            );
        );
    </script>

失败并出现错误:值 \"System.Collections.Generic.Dictionary`2[System.String,System.Object]\" 不是 \"WebApplication1.WebService1+Person\" 类型,不能在此使用通用集合。\r\n参数名称:值

那么我如何向 ASP.net 解释它实际上是一个 Person 数组?

请注意:将函数更改为 List(of Person) 或 ArrayList 确实有效,但鉴于我实现了自己的自定义集合,这对我来说不是最佳选择。

更新:好的,到目前为止,我发现这个问题肯定与 JavascriptSerializer 如何使用 SimpleTypeResolver 解析类型有关。基本上如果我做这样的事情

Public Function AddPersonList(ByVal PersonList As String) As PersonList

我可以使用以下代码重新创建错误。

Dim PersonList As PersonList = jsonSerializer.Deserialize(Of PersonList)(PList)

但是,当我提供自己的自定义类型解析器时,

Dim jsonSerializer As New javascriptSerializer(New MyCustomTypeResolver)

我可以成功创建自定义列表的实例。

现在我已经弄清楚了如何在 web.config 文件中提供我自己的自定义转换器。沿着这条线......。

<system.web.extensions>
    <scripting>
      <webServices>
        <jsonSerialization>
          <converters>
            <add name="MyCustomConverter" type="WebApplication1.MyCustomConverter" />
          </converters>
        </jsonSerialization>
      </webServices>      
    </scripting>
  </system.web.extensions>

但问题是我找不到指定 CustomTypeResolver 的方法。我的假设是,这是我遗漏的唯一难题。

PSS:我尝试使用 SimpleTypeResolver (SimpleTypeResolver MSDN) 文档中指定的程序集完全限定名称,但这会引发“由于对象的当前状态,操作无效”。异常 - 这是 TypeResolver 无法解析名称时导致的错误(我通过使用我的 CustomTypeResolver 进行测试知道这一点)

更新@奥列格和肖恩:

我衷心感谢您的意见,实际上已经更改了我的应用程序以反映您的建议;话虽如此,此更改的问题在于它需要以我希望避免的方式重新处理我的类实现及其相关行为。

我的问题从未将 List(of Object) 传递到我的 web 服务中(我只是提出这样的问题以简化 *** 的问题)。在这种情况下,我愿意完全接受使用 Generic List(of),但我的问题实际上是我的一个自定义对象实现了一个具有强类型 List(of) 的属性,例如:

Customer CustomerID AS Guid, FirstName AS String, LastName AS String, EmailAddresses AS EmailAddressList

现在需要改为

Customer CustomerID AS Guid, FirstName AS String, LastName AS String, EmailAddresses AS List(Of EmailAddress) 

诚然,这不是世界末日,并且在 web 服务上下文中可能更好(正如您所建议的那样),但对于我的 Collection 属性的内部应用程序使用而言,这绝对是不利的。这意味着,一旦我有了这个属性,我要么需要在每次我想使用一些高级功能时将它转换为我的 CustomCollectionList,要么我需要实现另一个暴露 CustomCollectionList 的属性。这两种解决方案都会在我嘴里留下难闻的味道。

【问题讨论】:

就我而言,这是 .NET 中用于 JSON 的序列化机制的限制。我还没有看到这个功能的例子,我自己也无法解决。投票后重新阅读问题 【参考方案1】:

首先是数据

 PersonList: [  FirstName: 'Max', LastName: 'Gershkovich' ,  FirstName: 'Test1', LastName: 'Test2'  ] 

您发送到网络服务的是错误的 JSON 数据。您可以验证http://www.jsonlint.com/ 上的数据。正确的 JSON 数据将是

"PersonList":["FirstName":"Max","LastName":"Gershkovich","FirstName":"Test1","LastName":"Test2"]

此外,我建议您永远不要手动制作 JSON。而不是你应该使用JSON.stringify。在大多数情况下,该功能甚至是网络浏览器中的natively supported(有时在更新后,如here)。正确序列化Web服务参数即可

$.ajax(
    data: PersonList:JSON.stringify(t) 
    // other parameters
);

(详情请参阅here)或

$.ajax(
    data: JSON.stringify( PersonList: t )
    // other parameters
);

在哪里

var t = [  FirstName: 'Max', LastName: 'Gershkovich' ,
           FirstName: 'Test1', LastName: 'Test2'  ];

哪个版本的数据表示JSON.stringify( PersonList: t )PersonList:JSON.stringify(t) 是正确的取决于其他事情。从那里可以轻松测试哪些在您的环境中有效。

下一个小问题:最好直接在AddPersonList的参数中使用List(Of Person),而不是使用继承自List(Of Person)的类型PersonList

更新:刚才我读到您的 cmets 关于 List(Of Person)PersonList 的另一个答案。为了防止同样的讨论,我决定写下我对此的看法。在 Web 服务中使用哪些类并不重要。您应该设计 Web 服务的界面,使其对每个对您的实现一无所知的人都简单明了。该方法的输入数据(至少您在问题中包含的数据)可以用List(Of Person) 完美描述。此外List(Of Person) 有利于数据传输。 在方法实现内部,您可以将List(Of Person) 转换为对方法内部有益的任何其他类。例如,从List(Of Person) 构造PersonList 非常简单。所以我建议你遵循这样的方式:保持 Web 服务的接口不受实现细节的影响。

更新 2:如果作为 Web 方法的输入参数的对象具有另一个对象(如 List(Of EmailAddress))作为属性,则根本不是问题。例如,如果您将上一个示例中的 EmailAddress 定义为

Public Class EmailAddress
    Public Property Type As String
    Public Property Address As String
End Class

Customer 喜欢

Public Class Customer
    'Public CustomerID As Guid
    Public Property FirstName As String
    Public Property LastName As String
    Public Property EmailAddresses As List(Of EmailAddress)
End Class

那么AddPersonList web 方法定义如下

<WebMethod()> _
Public Function AddPersonList(ByVal PersonList As List(Of Customer)) As String

同样的方式可以使用

<WebMethod()> _
Public Function AddPersonList1(ByVal input As InputData) As String

Public Class InputData
    Public Property PersonList As List(Of Customer)
End Class

或许多其他版本的对象,使用$.ajax 将信息发送到网络服务器。

在客户端你可以定义myData如下

var myData = [
         FirstName: 'Max', LastName: 'Gershkovich',
            EmailAddresses: [
                 Type: 'SMTP', Address: 'Max.Gershkovich@googlemail.com' ,
                 Type: 'SMTP', Address: 'Max.Gershkovich@mail.ru' ,
            ]
        ,
         FirstName: 'Test1', LastName: 'Test2' 
    ];

然后将数据发送到网络服务器

$.ajax(
    type: "POST",
    url: "WebService1.asmx/AddPersonList",
    data: JSON.stringify( PersonList: myData ),
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (data) 
        alert(data.d);
    ,
    error: function (xhr, textStatus, errorThrown) 
        alert("Error Occured!" + " | " + xhr.responseText + " | " +
              textStatus + " | " + errorThrown);
    
);

$.ajax(
    type: "POST",
    url: "WebService1.asmx/AddPersonList1",
    data: JSON.stringify( input: PersonList: myData ),
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function (data) 
        alert(data.d);
    ,
    error: function (xhr, textStatus, errorThrown) 
        alert("Error Occured!" + " | " + xhr.responseText + " | " +
              textStatus + " | " + errorThrown);
    
);

以上所有工作都没有任何问题。您可以下载the test example,它会执行此操作(应启动Default.htm。要调用该方法,您应单击第一个或第二个按钮)。

如果您查看客户端代码中定义的myData 对象,然后查看上面JSON.stringify 的用法,您会发现发送任何复杂的对象(包括数组和属性)都没有问题。在服务器端,您可以使用List(Of Customer)List(Of EmailAddress) 或其他对象列表作为 JavaScript 数组的表示形式。一切都会奏效。只有您原来的对象继承示例是不好的。

如果您尝试设计 Web 服务的接口从客户端而不是从内部服务器结构来看,您将很容易为 Web 方法的相应输入参数构造类。一切都会立即生效。如果您需要使用这些信息初始化您的内部类,您将能够非常简单地完成它。这种类型的数据转换将非常容易,您无需编写任何实际上相同的 CustomTypeResolver。

【讨论】:

【参考方案2】:

更新:第一步是从 .asmx 迁移到 WCF 的 .svc - 它更加灵活,并且它的序列化器更加智能。

我通常只使用一个通用列表参数,到目前为止这还没有让我失望。

如果您需要使用自定义ObjectList 属性而不是通用list&lt;Object&gt;,如何将ObjectList 属性从序列化中隐藏并在两者之间使用list&lt;Object&gt; 属性代理?

这样,序列化程序可以序列化和反序列化 list&lt;Object&gt; 属性,而您可以继续使用您的 ObjectList 属性。

这很容易在 WCF 中使用 DataMember 属性完成,通过使用 DataMember(Name='ObjectList'),您甚至可以在 js 中继续使用完全相同的属性名称。

总而言之,您将获得一个在 javascript 中表现为数组和在 .net 中表现为 ObjectList 的属性。

【讨论】:

抱歉,只是重读了您的想法(我知道这么久了),但它实际上也不错。可以标记属性 NotSerializable(或任何该属性)。【参考方案3】:

您的数据似乎不符合 JSON 格式。请尝试使用

data: "  PersonList: [  'FirstName': 'Max', 'LastName': 'Gershkovich' ,  'FirstName': 'Test1', 'LastName': 'Test2'  ] "

为了更安全,请使用 json 库将数据转换为 json。

【讨论】:

试过但同样的问题。它似乎可以毫无问题地解析 JSON。从错误消息来看,这在我看来像是某种类型解析错误,只是不确定如何正确地将数组映射到我的列表对象。 试过了,对我来说效果很好。您是否在 webservice 类中包含 属性? 是的,我做到了,您使用的代码是否完全相同?我的猜测是您使用了诸如 Public Function AddPersonList(ByVal PersonList As ArrayList) As String 之类的东西,这对我也很好用??? 是的。我使用 List(Of Person) 而不是 PersonList。使用从 List(Of Person).. 继承的类是否有特定原因? 是的,我实现了自己的自定义集合类。它们都是 List(Of) 但包含用于各种用途的自定义逻辑。 (通常用于数据库访问)所以不能离开他们 PS:非常感谢您的帮助。【参考方案4】:

只是猜测,您的数据字符串中是否有前导空格?在 " 和 之间。

data: "  PersonList: [  FirstName: 'Max', LastName: 'Gershkovich' ,  FirstName: 'Test1', LastName: 'Test2'  ] ",                 
       ^

尝试腾出空间。有什么变化吗?

【讨论】:

有....试过了,还是不行:-(同样的错误。在我看来,我需要注册一个自定义转换器并按照这些思路做一些事情。 空间不是问题。查看我的评论以获取可能的解决方案【参考方案5】:

虽然 Oleg 的回答似乎相当全面且是迄今为止最好的,但我想提供一种替代方法并推荐 .NET NewtonSoft JSON Library。

我同意让您的服务方法提供参数的清晰定义是件好事,但是,当我遇到问题并需要尽快让某些东西工作时,接受字符串并使用此库进行解析已经证明在紧要关头很有用。

【讨论】:

以上是关于ASP.Net webservices 中的 JSON 数组类型解析的主要内容,如果未能解决你的问题,请参考以下文章

ASP.Net webservices 中的 JSON 数组类型解析

ASP.net jQuery调用webservice返回json数据的一些问题

ASP.net Webservice *.asmx 访问与 WCF 服务参考

在asp.net里怎样异步调用WebService方法

什么时候在 asp.net WebService 中调用 Application_End

ASP.NET WebService - 实体框架