如何反序列化JSON与使用DataContractSerializer的类型收集无名 [英] How to deserialize JSON with unnamed collection of types using DataContractSerializer

查看:183
本文介绍了如何反序列化JSON与使用DataContractSerializer的类型收集无名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的Web服务,以获取有关路线里程的数据。然后我使用解串器解析出来。这里是JSON的样子:

I'm using web service to get data about route mileage. Then I'm using deserializer to parse it out. Here is how JSON looks:

[{"__type":"CalculateMilesReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"TMiles":445.5]

通过这种反应我有几个问题。为什么被包裹成收集和如何设置的对象模型?也有人抱怨特殊__type属性。所以,我没有破解和坦然字符串:

With this response I had couple issues. Why is is wrapped into collection and how do I set object model? Also it was complaining about special __type attribute. So, I did "hack" and "prepped" string:

// Cut off first and last charachters [] - they send objects as arrays
rawJSON = rawJSON.Substring(1, rawJSON.Length - 2);

// Hide "__type" attribute as it messes up serializer with namespace
rawJSON = rawJSON.Replace("__type", "type");



然后,一切都与此对象的工作:

Then everything worked with this object:

[DataContract]
public class PCMilerResponse
{
    [DataMember(Name = "Errors", EmitDefaultValue = false)]
    public PCMilerError[] Errors { get; set; }

    [DataMember(Name = "TMiles", EmitDefaultValue = false)]
    public decimal DrivingDistance { get; set; }    
}

现在我修改调用Web服务,我得到以下响应

Now I modified call to web service and I get following response

[
{"__type":"CalculateMilesReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"TMiles":445.5},
{"__type":"GeoTunnelReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"GeoTunnelPoints":
    [{"Lat":"34.730466","Lon":"-92.247147"},{"Lat":"34.704863","Lon":"-92.29329"},{"Lat":"34.676312","Lon":"-92.364654"},{"Lat":"29.664271","Lon":"-95.236735"}]
}
]

现在它是有道理的,为什么有数组和__type 。但我不知道怎么写对象正确地解析它。我猜特殊属性需要应用,也许通用阵列需要在那里?如何正确地反序列化任何帮助吗?

Now it makes sense why there is array and "__type". But I'm not sure how to write object to properly parse it. I guess special attributes need to be applied and maybe generic array need to be there? Any help on how to properly deserialize it?

P.S。我可以做更多的黑客攻击和替换这些字符串使其与2对象中的对象,但我不知道是否有正确的方法来处理它。

P.S. I can do more hacking and replace those strings making it object with 2 objects inside, but I wonder if there is "proper" way to handle it.

推荐答案

__类型参数是由 DataContractJsonSerializer 添加到多态代表类型信息。从文档

The "__type" parameter is added by DataContractJsonSerializer to represent polymorphic type information. From the docs:

多态性

多态性序列由序列化能力派生类型,其中其基本类型的预期。这是支持JSON序列通过WCF媲美支持XML序列化的方式。例如,您可以序列MyDerivedType哪里MyBaseType预期,或序列化诠释,其中对象预计...

Polymorphic serialization consists of the ability to serialize a derived type where its base type is expected. This is supported for JSON serialization by WCF comparable to the way XML serialization is supported. For example, you can serialize MyDerivedType where MyBaseType is expected, or serialize Int where Object is expected...

保留类型信息

如前所述,多态性是JSON支持有一些限制...

As stated earlier, polymorphism is supported in JSON with some limitations...

要保存类型标识,序列化时复杂类型JSON类型提示,可以添加,和解串器识别的提示和进行适当的操作。 类型提示是一个JSON键/值对__type的(两个下划线后跟字型)键名。该值的形式为JSON字符串DataContractName:DataContractNamespace(任何东西到第一个冒号的名称)。

To preserve type identity, when serializing complex types to JSON a "type hint" can be added, and the deserializer recognizes the hint and acts appropriately. The "type hint" is a JSON key/value pair with the key name of "__type" (two underscores followed by the word "type"). The value is a JSON string of the form "DataContractName:DataContractNamespace" (anything up to the first colon is the name).

为了使用这种机制来(德)序列多态类型,所有可能的派生类型必须指定在锋线上 DataContractJsonSerializer 。请参见数据协定已知类型的一如何做到这一点的讨论。

In order to use this mechanism to (de)serialize a polymorphic type, all possible derived types must be specified up front to DataContractJsonSerializer. See Data Contract Known Types for a discussion of how to do this.

因此,它看起来像您的Web服务将返回多态类型的数组。如何处理?

Thus, it looks like your web service is returning an array of polymorphic types. How to handle this?

手动解决方案

一个可能的解决您的问题是手动创建相应的数据接触层次C#类层次结构,以 DataContract 正确注释和数据成员属性。然后您可以利用数据契约序列化的类型提示功能,导致要反序列化过程中自动创建正确的子类。谷歌的礼貌,你看到的样子类以的 PC * MILER Web服务API:报告类。使用这个文件,你的类应该是这样的:

One possible solution to your problem is to manually create a c# class hierarchy corresponding to the data contact hierarchy, properly annotated with DataContract and DataMember attributes. Then you can leverage the "type hint" functionality of the data contract serializers to cause the correct subclass to be created automatically during deserialization. Courtesy of google, the classes you are seeing look to be documented at PC*MILER Web Services API: Report Class. Using this documentation, your classes should look like:

public static class Namespaces
{
    public const string Pcmiler = @"http://pcmiler.alk.com/APIs/v1.0";
}

[DataContract(Namespace = Namespaces.Pcmiler)]
public class Coordinates
{
    public double Lat { get; set; }
    public double Lon { get; set; }
}

[KnownType(typeof(CalculateMilesReport))]
[KnownType(typeof(GeoTunnelReport))]
[DataContract(Namespace = Namespaces.Pcmiler)]
public abstract class Report
{
    [DataMember]
    public string RouteID { get; set; }
}

[DataContract(Namespace = Namespaces.Pcmiler)]
public class CalculateMilesReport : Report
{
    [DataMember]
    public double TMiles { get; set; }
}

[DataContract(Namespace = Namespaces.Pcmiler)]
public class GeoTunnelReport : Report
{
    [DataMember]
    public List<Coordinates> GeoTunnelPoints { get; set; }
}



注意的 [KnownType(typeof运算(XXXReport))] 属性附加到报告。为了正确地反序列化JSON,的 报告的所有预期的子类必须作为已知类型的按照的文档有11个可能的子类,所以你需要为所有的人提供了类,您可能会收到来自您的Web服务。

Note the [KnownType(typeof(XXXReport))] attributes attached to Report. In order to deserialize the JSON correctly, all expected subclasses of Report must appear as known types. According to the documentation there are 11 possible subclasses, so you will need to provide classes for all of them that you might receive from your web service.

现在,你可以反序列化 rawJSON 列表<报告> ,一切都在你的样品JSON应该正确地阅读,因为你已经正确匹配的数据协定名称,命名空间和类型层次到Web服务的:

Now you can deserialize your rawJSON as a List<Report>, and everything in your sample JSON should read in correctly, because you have correctly matched the data contract names, namespaces, and type hierarchies to that of the web service:

        var list = DataContractJsonSerializerHelper.GetObject<List<Report>>(rawJSON);



使用

using

public static class DataContractJsonSerializerHelper
{
    private static MemoryStream GenerateStreamFromString(string value)
    {
        return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
    }

    public static T GetObject<T>(string json)
    {
        var serializer = new DataContractJsonSerializer(typeof(T));
        using (var stream = GenerateStreamFromString(json))
        {
            return (T)serializer.ReadObject(stream);
        }
    }
}



然而,Web服务< A HREF =http://pcmiler.alk.com/APIs/SOAP/v1.0/help/相对=nofollow>看起来相当精巧。手动重新创建它的所有类是烦人。

However, that web service looks rather elaborate. Manually recreating all its classes would be tiresome.

自动解决方案

由于它出现在你的Web服务是一个WCF服务,希望他们已经公布了其服务元数据。如果他们有,它可以让你生成客户端的自动的使用的添加服务引用的Visual Studio中。有关如何执行此操作说明,请参阅如何:创建一个Windows通讯基础客户端如何添加,更新,或删除服务引用

Since it appears your web service is a WCF service, hopefully they have published its Service Metadata. If they have, it will allow you to generate a client automatically using Add Service Reference in Visual Studio. For instructions on how to do this, see How to: Create a Windows Communication Foundation Client and How to: Add, Update, or Remove a Service Reference.

同样礼貌的谷歌,它出现在你的服务的的提供的元数据,在的 http://pcmiler.alk.com/APIs/REST/v1.0/service.svc?wsdl 。做

Again courtesy of google, it appears your service does provide its metadata, at http://pcmiler.alk.com/APIs/REST/v1.0/service.svc?wsdl. Doing

 svcutil.exe http://pcmiler.alk.com/APIs/REST/v1.0/service.svc?wsdl

似乎产生一个合理的设置与上面创建的手工类一致的客户端类。但是,你应该从你的Web服务doublecheck的文档,这确保消费其服务元数据的正确方法。

Seems to generate a plausible set of client classes consistent with the manual classes created above. However, you should doublecheck the documentation from your web service to ensure this the correct way to consume their service metadata.

一旦客户已经建立,可以访问网络服务,如果你是调用本地的C#API。请参见访问服务使用WCF客户端如何。文章创建和使用您的第一个WCF服务给出整个过程的概述

Once a client has been created, you can access the web service as if you were calling a local c# API. See Accessing Services Using a WCF Client for how. The article Creating and Consuming Your First WCF Service gives an overview of the entire process.

这篇关于如何反序列化JSON与使用DataContractSerializer的类型收集无名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆