提问人:Pebermynte Lars 提问时间:9/19/2023 最后编辑:Pebermynte Lars 更新时间:9/25/2023 访问量:87
如何将混合 JSON 和 XML 反序列化为类
How to deserialize mixed JSON and XML to classes
问:
我从Azure DevOps REST API收到一个响应字符串,该字符串主要包含JSON,并将一些XML散布到一些属性值中。我构建了许多 C# 类来复制响应的 JSON 结构,以便反序列化响应。
什么时候
var response = client.Get(request);
if (response.StatusCode == HttpStatusCode.OK
&& response.Content != null)
{
return JsonSerializer.Deserialize<T>(response.Content);
}
到达 XML
public class Fields
{
...
[JsonPropertyName("Microsoft.VSTS.TCM.Steps")]
public Steps MicrosoftVSTSTCMSteps { get; set; }
}
我得到一个
System.Text.Json.JsonException: 'The JSON value could not be converted to ADO.Response.Steps. Path: $.fields['Microsoft.VSTS.TCM.Steps'] | LineNumber: 0 | BytePositionInLine: 5149.'
Steps 类的定义如下
[XmlRoot(ElementName = "steps")]
public class Steps
{
[XmlElement(ElementName = "step")]
public Step[] Step { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "last")]
public string Last { get; set; }
...
}
传入的 JSON/XML 字符串的 exerpt 如下所示:
{
...
"Microsoft.VSTS.TCM.Steps":"<?xml version="1.0"?><steps last="5" id="0"><step id="2" type="ActionStep"><parameterizedString isformatted="true"></parameterizedString><description /></step></steps><xml/>
...
}
如何在不将 Steps 类与层次结构分离的情况下反序列化 JSON 和 XML,例如将 XML 保留为反序列化 JSON 中的字符串?
我希望避免这样的后期处理
XmlSerializer XmlSerializer = new XmlSerializer(typeof(Steps));
Steps steps = (Steps)XmlSerializer.Deserialize(new StringReader(resp.fields.MicrosoftVSTSTCMSteps));
答:
请参阅下面的解决方案
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Text.Json;
namespace ConsoleApp10
{
class Program
{
const string FILENAME = @"c:\temp\myprogram.xml";
static void Main(string[] args)
{
}
}
[XmlRoot(ElementName = "steps")]
public class Steps : IXmlSerializable
{
[XmlElement(ElementName = "step")]
public Step[] Step { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlAttribute(AttributeName = "last")]
public string Last { get; set; }
public JsonElement json { get; set; }
[XmlText]
public string text
{
get { return JsonSerializer.Serialize(json); }
set { json = (JsonElement) JsonSerializer.Deserialize(value, typeof(JsonElement)); }
}
public void WriteXml(XmlWriter writer)
{
}
public void ReadXml(XmlReader reader)
{
}
public XmlSchema GetSchema()
{
return (null);
}
}
public class Step
{
}
}
评论
使用 Newtonsoft.Json.JsonConvert.SerializeXNode 将 xml 转换为 json 要容易得多
var jNode = System.Text.Json.Nodes.JsonObject.Parse(content);
var fields = System.Text.Json.JsonSerializer.Deserialize<Fields>(jNode);
var json = Newtonsoft.Json.JsonConvert.SerializeXNode(XElement.Parse((string)jNode["Microsoft.VSTS.TCM.Steps"]));
fields.MicrosoftVSTSTCMSteps = System.Text.Json.JsonSerializer.Deserialize<Root>(json).steps;
类
public class Fields
{
[System.Text.Json.Serialization.JsonIgnore]
public Steps MicrosoftVSTSTCMSteps { get; set; }
}
public class Root
{
public Steps steps { get; set; }
}
public class ParameterizedString
{
[JsonPropertyName("@isformatted")]
public string Isformatted { get; set; }
}
public class Step
{
[JsonPropertyName("@id")]
public string Id { get; set; }
[JsonPropertyName("@type")]
public string Type { get; set; }
[JsonPropertyName("parameterizedString")]
public ParameterizedString ParameterizedString { get; set; }
}
public class Steps
{
[JsonPropertyName("@last")]
public string Last { get; set; }
[JsonPropertyName("@id")]
public string Id { get; set; }
[JsonPropertyName("step")]
public Step Step { get; set; }
}
自定义转换器成为我最好的解决方案,使用 和 。
我现在可以使用常规属性属性,并将反序列化保持在一个整洁的流程中。System.Text.Json
System.Text.Json.Serialization
System.XML.Serialization
// Request an ADO resource and deserialize the JSON response with embedded XML into C# classes.
private static T? Get<T>(string resource, RestClient client)
{
var request = new RestRequest()
{
Resource = resource
};
var response = client.Get(request);
if (response.StatusCode == HttpStatusCode.OK
&& response.Content != null)
{
// Create a custom deserializer for mixed json and xml content
var options = new JsonSerializerOptions()
{
Converters = { new MixedJsonXmlConverter() }
};
// Deserialize mixed json and xml content from response
return JsonSerializer.Deserialize<T>(response.Content, options);
}
return default;
}
选取包含面向反序列化的 XML 的属性的自定义转换器如下所示:Steps
internal class MixedJsonXmlConverter : JsonConverter<Steps>
{
// Deserialize Microsoft.VSTSTCMSteps of Steps data type stored in the JSON response, as XML.
// https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/converters-how-to?pivots=dotnet-6-0#registration-sample---converters-collection
// Support property attributes equal to stock deserialization of JSON and XML.
public override Steps? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// Read XML string and parse it to object of type Steps
var xml = reader.GetString();
var serializer = new XmlSerializer(typeof(Steps));
using var stringReader = new StringReader(xml);
return (Steps)serializer.Deserialize(stringReader);
}
public override void Write(Utf8JsonWriter writer, Steps value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
如果我遇到其他需要反序列化为 XML 的 JSON 属性,我可以使用相同的模式。
因此,现在我可以将响应作为一个有凝聚力的数据结构来处理,例如:
var workItem = Get<CaseWorkItem>(resource, client);
Console.WriteLine($"Property in JSON response: {workItem.fields.SystemAreaPath}");
Console.WriteLine($"Property in XML response: {workItem.fields.MicrosoftVSTSTCMSteps.Id}")
评论
{"foo": "<?xml version=\"1.0\"><steps last=\"5\" id=\"0\">...<\steps></xml>" }