gpt4 book ai didi

c# - 在 Web API 中将包含 JToken 的对象序列化为 XML 时出现循环引用异常

转载 作者:数据小太阳 更新时间:2023-10-29 02:23:28 24 4
gpt4 key购买 nike

在我的数据库中,我有一个包含很多列的表,其中一列包含一个 JSON 字符串(我无法控制它)。像这样:

Name  Age  ExtraData
---- --- ------------------
Bob 31 {c1: "1", c2: "2"} <-- string with JSON

我的 Web API 端点必须根据请求中的 Accept header 返回 XML 或 JSON。像这样:

JSON:

{
"Name": "Bob",
"Age": 31,
"ExtraData": {
"c1": 1,
"c2": 2
}
}

XML:

<person>
<Name>Bob</Name>
<Age>31</Age>
<ExtraData>
<c1>1</c1>
<c2>2</c2>
</ExtraData>
</person>

为此,我在 C# 中创建了一个这样的类:

public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Object ExtraData { get; set; }
}

当从数据库解析数据时,我会这样填写ExtraData:

personInstance.ExtraData = JsonConvert.DeserializeObject(personTableRow.ExtraData);

当 Web API 返回 JSON 时,一切都按预期工作。

当 Web API 返回 XML 时,它给出一个异常:

The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.

内部异常是这样的(不是英文的):

Newtonsoft.Json.Linq.JToken has a circular reference and that is not supported. (O tipo 'Newtonsoft.Json.Linq.JToken' é um contrato de dados de coleção recursiva que não é suportado. Considere modificar a definição da coleção 'Newtonsoft.Json.Linq.JToken' para remover referências a si mesma.)

有没有办法在没有循环引用的情况下将 JSON 数据解析为对象?

最佳答案

您遇到了 XmlSerializer 的限制。将 JSON 对象(这是一组无序的名称/值对,用大括号括起来)反序列化为 c# object 时,Json.NET 创建一个类型为 JObject 的对象,不幸的是 XmlSerializer 不知道如何序列化 JObject。特别是它陷入无限递归试图序列化 JToken.Parent 的 child 。因此,您需要将底层 object ExtraData 转换为 XmlSerializer 可以处理的类型。

但是,使用什么类型并不明显,因为:

您可以利用 [XmlAnyElement] 功能创建一个代理属性,使用 Json.NET 的 XElement 将您的 对象 ExtraDataXmlNodeConverter 相互转换:

public class Person
{
public string Name { get; set; }
public int Age { get; set; }

[XmlIgnore]
[JsonProperty]
public object ExtraData { get; set; }

[XmlAnyElement("ExtraData")]
[JsonIgnore]
public XElement ExtraDataXml
{
get
{
return JsonExtensions.SerializeExtraDataXElement("ExtraData", ExtraData);
}
set
{
ExtraData = JsonExtensions.DeserializeExtraDataXElement("ExtraData", value);
}
}
}

public static class JsonExtensions
{
public static XElement SerializeExtraDataXElement(string name, object extraData)
{
if (extraData == null)
return null;
var token = JToken.FromObject(extraData);
if (token is JValue)
{
return new XElement(name, (string)token);
}
else if (token is JArray)
{
return new JObject(new JProperty(name, token)).ToXElement(false, name, true);
}
else
{
return token.ToXElement(false, name, true);
}
}

public static object DeserializeExtraDataXElement(string name, XElement element)
{
object extraData;
if (element == null)
extraData = null;
else
{
extraData = element.ToJToken(true, name, true);
if (extraData is JObject)
{
var obj = (JObject)extraData;
if (obj.Count == 1 && obj.Properties().First().Name == name)
extraData = obj.Properties().First().Value;
}
if (extraData is JValue)
{
extraData = ((JValue)extraData).Value;
}
}
return extraData;
}

public static XElement ToXElement(this JToken obj, bool omitRootObject, string deserializeRootElementName, bool writeArrayAttribute)
{
if (obj == null)
return null;
using (var reader = obj.CreateReader())
{
var converter = new Newtonsoft.Json.Converters.XmlNodeConverter() { OmitRootObject = omitRootObject, DeserializeRootElementName = deserializeRootElementName, WriteArrayAttribute = writeArrayAttribute };
var jsonSerializer = JsonSerializer.CreateDefault(new JsonSerializerSettings { Converters = { converter } });
return jsonSerializer.Deserialize<XElement>(reader);
}
}

public static JToken ToJToken(this XElement xElement, bool omitRootObject, string deserializeRootElementName, bool writeArrayAttribute)
{
// Convert to Linq to XML JObject
var settings = new JsonSerializerSettings { Converters = { new XmlNodeConverter { OmitRootObject = omitRootObject, DeserializeRootElementName = deserializeRootElementName, WriteArrayAttribute = writeArrayAttribute } } };
var root = JToken.FromObject(xElement, JsonSerializer.CreateDefault(settings));
return root;
}
}

使用上面的类,我可以反序列化您的 JSON 并序列化为 XML,结果如下:

<Person>
<Name>Bob</Name>
<Age>31</Age>
<ExtraData>
<c1>1</c1>
<c2>2</c2>
</ExtraData>
</Person>

请注意,JSON 和 XML 之间存在导致问题的不一致:

  • JSON 原始值是“轻微”类型化的(如字符串、数字、 bool 值或空值),而 XML 文本是完全无类型的。因此,JSON 中的数值(和日期)作为字符串往返到 XML。

  • XML 没有数组的概念。因此,根容器为数组的 JSON 需要在序列化期间添加合成根元素。这会在转换过程中增加一些代码味道。

  • XML 必须有一个根元素,而有效的 JSON 可以包含原始值,例如字符串。在转换过程中再次需要合成根元素。

经过轻微测试的原型(prototype) fiddle here ,我在其中演示代码适用于 ExtraData,它是一个 JSON 对象、一个字符串数组、一个字符串和一个 null值(value)。

关于c# - 在 Web API 中将包含 JToken 的对象序列化为 XML 时出现循环引用异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40978662/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com