C# 如何使用DataContractSerializer使用未命名的类型集合反序列化JSON
我正在使用web服务获取有关路线里程的数据。然后我使用反序列化程序来解析它。以下是JSON的外观:C# 如何使用DataContractSerializer使用未命名的类型集合反序列化JSON,c#,.net,json,datacontractserializer,datacontractjsonserializer,C#,.net,Json,Datacontractserializer,Datacontractjsonserializer,我正在使用web服务获取有关路线里程的数据。然后我使用反序列化程序来解析它。以下是JSON的外观: [{"__type":"CalculateMilesReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"TMiles":445.5] 我的回答有两个问题。为什么要包装到集合中?如何设置对象模型?它还抱怨特殊类型的属性。所以,我做了“黑客”和“预备”字符串: 然后一切都与这个对象一起工作: [DataContract] publ
[{"__type":"CalculateMilesReport:http:\/\/pcmiler.alk.com\/APIs\/v1.0","RouteID":null,"TMiles":445.5]
我的回答有两个问题。为什么要包装到集合中?如何设置对象模型?它还抱怨特殊类型的属性。所以,我做了“黑客”和“预备”字符串:
然后一切都与这个对象一起工作:
[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服务的调用,并得到以下响应
[
{"__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"}]
}
]
现在有了数组和“\uuuu类型”的道理了。但我不知道如何编写对象来正确解析它。我猜需要应用特殊属性,也许需要有通用数组?有关于如何正确反序列化它的帮助吗
另外,我可以进行更多的黑客攻击,并用2个对象替换那些使其成为对象的字符串,但我想知道是否有“正确”的方法来处理它。关于使
\u type
属性消失,有关于这方面的讨论
,其解决方法如下:
将WebMethod返回类型更改为object,即
[WebMethod]
public static object ApplyCredits(int addonid, int[] vehicleIds)
而不是
[WebMethod]
public static WebMethodReturn ApplyCredits(int addonid, int[] veh
解决者
将命名空间参数[DataContract(namespace=”“)]
添加到数据协定中
我不知道如何编写对象来正确解析它
基于响应,您可以构建适合JSON的类,但是由于您有模型类,因此您应该使用构建JSON的模型类。也许我没有从你的问题中得到正确的答案
下面是一个精心制作的模型示例,您的JSON将适用于此示例:
public class ResultType
{
public string RouteID { get; set; }
public List<GeoTunnelPoints> Points { get; set; }
public double TMiles { get; set; }
public ResultType()
{
RouteID = "";
Points = new List<GeoTunnelPoints>();
TMiles = 0;
}
}
public class GeoTunnelPoints
{
double Lat { get; set; }
double Lon { get; set; }
public GeoTunnelPoints()
{
Lat = 0.0;
Lon = 0.0;
}
}
公共类结果类型
{
公共字符串RouteID{get;set;}
公共列表点{get;set;}
公共双T文件{get;set;}
公共结果类型()
{
RouteID=“”;
点=新列表();
t文件=0;
}
}
公共级土工隧道点
{
双Lat{get;set;}
双Lon{get;set;}
公共土工隧道点()
{
Lat=0.0;
Lon=0.0;
}
}
用法示例:
// Your example JSON after excluding the __type
string input =
"[" +
"{" +
"\"RouteID\":null, " +
"\"TMiles\":445.5}," +
"{" +
"\"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\"}" +
"]" +
"} " +
"]";
List<ResultType> resultList = new List<ResultType>();
// This will be your C# result collection
resultList = new JavaScriptSerializer().Deserialize<List<ResultType>>(input);
//排除_类型后的JSON示例
字符串输入=
"[" +
"{" +
“\”路由ID\”:空+
“\'TMiles\':445.5}”+
"{" +
“\”路由ID\”:空+
“\“土工隧道点\”:”+
"[" +
“{\'Lat\':\'34.730466\',\'Lon\':\'-92.247147\'”+
“{\'Lat\':\'34.704863\',\'Lon\':\'-92.29329\'”+
“{\'Lat\':\'34.676312\',\'Lon\':\'-92.364654\'”+
“{\'Lat\':\'29.664271\',\'Lon\':\'-95.236735\'”+
"]" +
"} " +
"]";
列表结果列表=新列表();
//这将是您的C#结果集合
resultList=new JavaScriptSerializer()。反序列化(输入);
数据契约序列化器添加了参数“\uuuu type”
以表示多态类型信息。从:
多态性
多态序列化包括在需要派生类型的基类型时序列化派生类型的能力。WCF对JSON序列化的支持与支持XML序列化的方式相当。例如,您可以在需要MyBaseType的地方序列化MyDerivedType,或者在需要Object的地方序列化Int
保留类型信息
如前所述,JSON支持多态性,但有一些限制
为了保留类型标识,将复杂类型序列化为JSON时,可以添加一个“类型提示”,反序列化程序可以识别提示并适当地执行操作。“type hint”是一个JSON键/值对,键名为“uuu type”(两个下划线后跟单词“type”)。该值是一个JSON字符串,格式为“DataContractName:DataContractNamespace”(第一个冒号之前的任何内容都是名称)
为了使用此机制(反)序列化多态类型,必须在DataContractJsonSerializer
之前指定所有可能的派生类型。有关如何执行此操作的讨论,请参阅
因此,看起来您的web服务正在返回一个多态类型数组。如何处理
手动解决方案
解决此问题的一个可能方法是手动创建一个与数据联系人层次结构相对应的c类层次结构,并使用DataContract
和DataMember
属性进行适当注释。然后,您可以利用数据协定序列化程序的“类型提示”功能,在反序列化过程中自动创建正确的子类。承蒙谷歌的帮助,你所看到的课程看起来都是文档化的。使用此文档,您的类应该如下所示:
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; }
}
使用
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服务中的文档,以确保这是使用其服务元数据的正确方法
一旦创建了客户机,就可以像调用本地c#API一样访问web服务。看看怎么做。这篇文章概述了整个过程。JSON的结构似乎与您的类结构并不匹配。如果您看看我如何“破解”JSON,那么它确实匹配。在本例中,JSON没有任何错误,因此属性将被解析为null。但是TMiles在那里,反序列化后将在pcmilerrresponse上。DataContract属性没有任何参数吗?顺便说一句,在“objecst”我想你是想写“objects”,我不知道我的查找程序为什么这样键入:)
var list = DataContractJsonSerializerHelper.GetObject<List<Report>>(rawJSON);
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);
}
}
}
svcutil.exe http://pcmiler.alk.com/APIs/REST/v1.0/service.svc?wsdl