NewtonsoftJson 中的自定义 JSONConverter 用于序列化
Posted
技术标签:
【中文标题】NewtonsoftJson 中的自定义 JSONConverter 用于序列化【英文标题】:Custom JSONConverter in NewtonsoftJson for Serialization 【发布时间】:2022-01-14 22:51:00 【问题描述】:我想在 newtonsoftjson 库中编写自定义 JSON 转换器以进行序列化:
要求sn-p:
"Roles":[
"Role":[
"LEAssociateTypeId":"101",
"LEAssociateTypeId_Value":"Client/Counterparty",
"LastUpdatedDate":"2021-11-30T08:35:01",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
,
"LEAssociateTypeId":"5501",
"LEAssociateTypeId_Value":"Principal",
"LastUpdatedDate":"2021-11-29T08:50:34",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
]
]
收件人:
"Roles":[
"Role":
"LEAssociateTypeId":"101",
"LEAssociateTypeId_Value":"Client/Counterparty",
"LastUpdatedDate":"2021-11-30T08:35:01",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
,
"Role":
"LEAssociateTypeId":"5501",
"LEAssociateTypeId_Value":"Principal",
"LastUpdatedDate":"2021-11-29T08:50:34",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
]
由于 Roles 是一个动态对象,不能为其定义对应的类。
我还检查了在线文档 Role is converted to a array as per as https://www.newtonsoft.com/json/help/html/convertingjsonandxml.html
源代码编写:
public class customconverter : JsonConverter
public override bool CanConvert(Type objectType)
return true;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
throw new NotImplementedException();
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
t.WriteTo(writer);
else
JObject o = (JObject)t;
foreach(var a in _validcollectionList)
if (o[a] != null && o[a].Count() > 0)
var test = o[a][0];
var test1 = test["Role"];
var test2 = o[a] as JArray;
if (test1 != null && test1.Count() > 1)
foreach (var a1 in test1)
JObject obj = new JObject
"Role", a1
;
test2.Add(obj);
test.Remove();
o.WriteTo(writer);
[2021 年 11 月 12 日] 更新:准确地说完整的 json 就像:
"Message":
"MessageInfo":
"Guid": "a2152d96-c202-4c08-a4a7-2331a648b586",
"SourceId": "101",
,
"Roles":[
"Role":[
"LEAssociateTypeId":"101",
"LEAssociateTypeId_Value":"Client/Counterparty",
"LastUpdatedDate":"2021-11-30T08:35:01",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
,
"LEAssociateTypeId":"5501",
"LEAssociateTypeId_Value":"Principal",
"LastUpdatedDate":"2021-11-29T08:50:34",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
]
]
实体或属性的数量也是动态的,因此上述可能会有所不同。
已检查解决方案,以下代码行没有角色详细信息 =
var semiParsedJson = JObject.Parse(json);
只有 messageinfo 存在,所以它没有解析完整的 json。
我们也不能通过自定义 json 转换器来做,因为最初的目的是通过下面的代码行将 xml 转换为 json:
XmlDocument doc = new XmlDocument();
doc.Load("XMLFile1.xml");
string jsonText = JsonConvert.SerializeXmlNode(doc, Newtonsoft.Json.Formatting.Indented);
但不知何故,newtonsoftjson 库将同一级别的同名节点分组到一个数组中,这就是这个查询出现的原因。
请提出建议。
[12/12/2021]:示例 XML 片段:
<Message>
<MessageInfo>
<Guid>be190914-4b18-4454-96ec-67887dd4d7a7</Guid>
<SourceId>101</SourceId>
</MessageInfo>
<LegalEntities>
<LegalEntity>
<Roles>
<Role>
<LEAssociateTypeId>101</LEAssociateTypeId>
<LEAssociateTypeId_Value>Client/Counterparty</LEAssociateTypeId_Value>
<LastUpdatedDate>2021-08-07T23:05:17</LastUpdatedDate>
<LegalEntityRoleStatusId>3</LegalEntityRoleStatusId>
<LegalEntityRoleStatusId_Value>Active</LegalEntityRoleStatusId_Value>
</Role>
<Role>
<LEAssociateTypeId>6000</LEAssociateTypeId>
<LEAssociateTypeId_Value>Account Owner</LEAssociateTypeId_Value>
<LastUpdatedDate>2021-08-07T21:20:07</LastUpdatedDate>
<LegalEntityRoleStatusId>3</LegalEntityRoleStatusId>
<LegalEntityRoleStatusId_Value>Active</LegalEntityRoleStatusId_Value>
</Role>
<Role>
<LEAssociateTypeId>5003</LEAssociateTypeId>
<LEAssociateTypeId_Value>Investment Manager</LEAssociateTypeId_Value>
<LastUpdatedDate>2021-08-16T06:12:59</LastUpdatedDate>
<LegalEntityRoleStatusId>3</LegalEntityRoleStatusId>
<LegalEntityRoleStatusId_Value>Active</LegalEntityRoleStatusId_Value>
</Role>
</Roles>
</LegalEntity>
</LegalEntities>
</Message>
【问题讨论】:
欢迎来到 ***。我已将您的 json 样本修复为有效样本,希望您不要介意。 很抱歉,您的编辑带来的混乱多于清晰。现在您在谈论 xml 文件,还有一个新的 json 文件...我可以再次更改我提出的解决方案以适应您的需求,但我需要知道是否有任何其他要求或细节我需要注意。 由于角色下有动态字段,因此无法为此定义模型。自定义类型转换器用于特定类型以覆盖默认的序列化和反序列化逻辑。在您的情况下,您想将 json 转换为另一个,因此如果我对您的问题的理解正确,则不涉及序列化。 提供的初始 json 只是 sn-p 不是完整的 json ,除了角色实体之外,还可以有其他实体需要应用相同的逻辑,之后我可能可以循环遍历替代解决方案 添加了示例 XML 片段,请帮助 【参考方案1】:我留下另一篇帖子,因为 OP 的问题与原始版本相比发生了很大变化。
只是为了确保我们的理解是一样的:
您想使用JsonConvert.SerializeXNode
将xml 转换为json
默认情况下,此方法不会优雅地处理数组
Related documentation
Related github issue
您还想更改 Role
节点的表示,使其周围有一个包装器对象
让我们一个一个解决问题
XML 节点到 Json 数组
为了将 LegalEntities
和 Roles
视为数组,您需要向这些 xml 节点添加自定义属性:json:Array = "true"
。
json
命名空间必须在 xml 中定义,如下所示:xmlns:json="http://james.newtonking.com/projects/json"
因此,您需要将 xml 修改为此(手动或通过 System.Xml
/ System.Xml.Linq
):
<Message xmlns:json="http://james.newtonking.com/projects/json">
<MessageInfo>
<Guid>be190914-4b18-4454-96ec-67887dd4d7a7</Guid>
<SourceId>101</SourceId>
</MessageInfo>
<LegalEntities json:Array="true">
<LegalEntity>
<Roles json:Array="true">
<Role>
<LEAssociateTypeId>101</LEAssociateTypeId>
<LEAssociateTypeId_Value>Client/Counterparty</LEAssociateTypeId_Value>
<LastUpdatedDate>2021-08-07T23:05:17</LastUpdatedDate>
<LegalEntityRoleStatusId>3</LegalEntityRoleStatusId>
<LegalEntityRoleStatusId_Value>Active</LegalEntityRoleStatusId_Value>
</Role>
<Role>
<LEAssociateTypeId>6000</LEAssociateTypeId>
<LEAssociateTypeId_Value>Account Owner</LEAssociateTypeId_Value>
<LastUpdatedDate>2021-08-07T21:20:07</LastUpdatedDate>
<LegalEntityRoleStatusId>3</LegalEntityRoleStatusId>
<LegalEntityRoleStatusId_Value>Active</LegalEntityRoleStatusId_Value>
</Role>
<Role>
<LEAssociateTypeId>5003</LEAssociateTypeId>
<LEAssociateTypeId_Value>Investment Manager</LEAssociateTypeId_Value>
<LastUpdatedDate>2021-08-16T06:12:59</LastUpdatedDate>
<LegalEntityRoleStatusId>3</LegalEntityRoleStatusId>
<LegalEntityRoleStatusId_Value>Active</LegalEntityRoleStatusId_Value>
</Role>
</Roles>
</LegalEntity>
</LegalEntities>
</Message>
现在,如果您将其传递给SerializeXNode
,那么您将获得以下 json:
"Message":
"MessageInfo":
"Guid":"be190914-4b18-4454-96ec-67887dd4d7a7",
"SourceId":"101"
,
"LegalEntities":[
"LegalEntity":
"Roles":[
"Role":[
"LEAssociateTypeId":"101",
"LEAssociateTypeId_Value":"Client/Counterparty",
"LastUpdatedDate":"2021-08-07T23:05:17",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
,
"LEAssociateTypeId":"6000",
"LEAssociateTypeId_Value":"Account Owner",
"LastUpdatedDate":"2021-08-07T21:20:07",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
,
"LEAssociateTypeId":"5003",
"LEAssociateTypeId_Value":"Investment Manager",
"LastUpdatedDate":"2021-08-16T06:12:59",
"LegalEntityRoleStatusId":"3",
"LegalEntityRoleStatusId_Value":"Active"
]
]
]
变换Role
节点
为了能够执行一些转换,您需要通过SelectToken
方法检索适当的节点
var doc = XDocument.Parse(xml);
var json = JsonConvert.SerializeXNode(doc);
var root = JObject.Parse(json);
var roles = root.SelectToken("$.Message.LegalEntities[0].LegalEntity.Roles") as JArray;
if (roles == null) return;
var role = roles.First().SelectToken("$.Role") as JArray;
if (roles == null) return;
第一个 SelectToken
检索到 Roles
集合
第二个SelectToken
检索到Role
集合
现在,让我们进行转换:
var roleNodes = new List<JObject>();
foreach (var roleNode in role)
roleNodes.Add(new JObject(new JProperty("Role", roleNode)));
在这里,我们遍历Role
集合,并围绕它创建一个包装对象,该对象具有一个名为 Role 的属性。
最后我们必须用新创建的JObject
s 替换Roles
集合。这可以通过以下简单命令来实现:
roles.ReplaceAll(roleNodes);
为了完整起见,这里是完整的代码:
var xml = File.ReadAllText("sample.xml");
var doc = XDocument.Parse(xml);
var json = JsonConvert.SerializeXNode(doc);
var root = JObject.Parse(json);
var roles = root.SelectToken("$.Message.LegalEntities[0].LegalEntity.Roles") as JArray;
if (roles == null) return;
var role = roles.First().SelectToken("$.Role") as JArray;
if (roles == null) return;
var roleNodes = new List<JObject>();
foreach (var roleNode in role)
roleNodes.Add(new JObject(new JProperty("Role", roleNode)));
roles.ReplaceAll(roleNodes);
Console.WriteLine(root);
以及发出的输出:
"Message":
"MessageInfo":
"Guid": "be190914-4b18-4454-96ec-67887dd4d7a7",
"SourceId": "101"
,
"LegalEntities": [
"LegalEntity":
"Roles": [
"Role":
"LEAssociateTypeId": "101",
"LEAssociateTypeId_Value": "Client/Counterparty",
"LastUpdatedDate": "2021-08-07T23:05:17",
"LegalEntityRoleStatusId": "3",
"LegalEntityRoleStatusId_Value": "Active"
,
"Role":
"LEAssociateTypeId": "6000",
"LEAssociateTypeId_Value": "Account Owner",
"LastUpdatedDate": "2021-08-07T21:20:07",
"LegalEntityRoleStatusId": "3",
"LegalEntityRoleStatusId_Value": "Active"
,
"Role":
"LEAssociateTypeId": "5003",
"LEAssociateTypeId_Value": "Investment Manager",
"LastUpdatedDate": "2021-08-16T06:12:59",
"LegalEntityRoleStatusId": "3",
"LegalEntityRoleStatusId_Value": "Active"
]
]
【讨论】:
感谢您的回答,让我检查一下 嗨,代码行:var root = JObject.Parse(jsonText);没有解析整个 json,我只看到 MessageInfo 实体 @tusharbeniwal 您的 jsonText 变量是否包含所有数据?那么在尝试半解析之前,输入是否正确? 再次检查,现在正在为提供的解决方案工作,将进一步更新 以上解决方案提供的工作,谢谢【参考方案2】:如果我的理解是正确的,那么你想做以下事情:
Roles
应该有两个孩子而不是一个
Role
不应该是一个集合,而是一个属性
我认为最简单的方法是通过Linq to Json
static void Main(string[] args)
var json = "...";
var semiParsedJson = JObject.Parse(json);
Console.WriteLine(TranformJson(semiParsedJson));
static string TranformJson(JObject semiParsed)
if (!semiParsed.TryGetValue("Roles", out var roles))
return null;
if (roles is not JArray rolesArray)
return null;
var firstRole = (JObject)rolesArray.First();
if (!firstRole.TryGetValue("Role", out var role))
return null;
if (role is not JArray roleArray)
return null;
var newStructure = new
Roles = roleArray.Children()
.Select(role => new Role = role )
;
return JsonConvert.SerializeObject(newStructure);
-
我们将 json 解析为
JObject
以帮助我们检索特定的属性/集合
我们尝试检索Roles
属性,如果它不存在则返回null
如果它存在,那么我们检查它是否是一个集合。我在这里使用了 C# 9 的新 is not
功能
如果不是集合,则返回null
如果它是一个集合,则我们从中检索第一个元素
我们执行相同的步骤 (2-4) 来检索 Role
集合
我们创建一个新的匿名对象来以所需的方式表示数据
我们还对最内部的对象进行了一些转换
最后我们将新构造的对象序列化为字符串,得到想要的输出
享受吧! :)
【讨论】:
谢谢回复,让我查一下 @tusharbeniwal 对你有用吗? 我有问题更新了,请帮忙以上是关于NewtonsoftJson 中的自定义 JSONConverter 用于序列化的主要内容,如果未能解决你的问题,请参考以下文章