如何将混合 JSON 和 XML 反序列化为类

How to deserialize mixed JSON and XML to classes

提问人:Pebermynte Lars 提问时间:9/19/2023 最后编辑:Pebermynte Lars 更新时间:9/25/2023 访问量:87

问:

我从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));

C# JSON XML 反序列化

评论

0赞 Serge 9/20/2023
json 和 xml 字符串在哪里?如果我们不知道我们试图去解冻什么,您认为我们能为您提供什么帮助?
0赞 jdweng 9/20/2023
请参阅以下内容:learn.microsoft.com/en-us/dotnet/api/...
0赞 Pebermynte Lars 9/20/2023
@jdweng 如果你有一个例子来说明这对我有什么作用,我很乐意接受它作为答案。
1赞 derpirscher 9/20/2023
XML 真的只是在 JSON 中未转义地坐在那里吗?即使是 MS 也不会产生这样的混乱......我想它看起来更像是这样,即 XML 被嵌入为转义字符串,对吧?{"foo": "<?xml version=\"1.0\"><steps last=\"5\" id=\"0\">...<\steps></xml>" }
1赞 derpirscher 9/20/2023
通常,您可以使用自定义转换器来解决此类问题:通过文档工作并尝试一下。如果你没有让它工作,请在这里发布你的代码......

答:

0赞 jdweng 9/20/2023 #1

请参阅下面的解决方案

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
    {

    }

}

评论

0赞 Pebermynte Lars 9/20/2023
写得好。看起来这是针对 XML 中的 JSON。我在JSON中有XML。
1赞 Serge 9/21/2023 #2

使用 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; }
}
0赞 Pebermynte Lars 9/25/2023 #3

自定义转换器成为我最好的解决方案,使用 和 。 我现在可以使用常规属性属性,并将反序列化保持在一个整洁的流程中。System.Text.JsonSystem.Text.Json.SerializationSystem.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}")