XML 使用 C# 代码和 F# 模型进行反序列化
XML deserialization using C# code and F# models
我有将某些模型从 C# 迁移到 F# 的不寻常要求,老实说,我在 XML 反序列化方面遇到了一些困难。这是我到目前为止所拥有的。以下 XML + 反序列化 C# 代码 + F# 模型的配对非常有效:
<nameServers>
<hostNames>
<AddressWrapper>
<Address>www.blah1.com</Address>
</AddressWrapper>
<AddressWrapper>
<Address>www.blah2.com</Address>
</AddressWrapper>
</hostNames>
</nameServers>
[<CLIMutable>]
[<XmlTypeAttribute("AddressWrapper")>]
type AddressWrapper =
{ [<XmlElement("Address")>]
Address: string }
[<CLIMutable>]
type WhoisRecordNameServersV2 =
{ [<XmlArray("hostNames")>]
hostNames: AddressWrapper [] }
以下只是随笔,不要对代码附加任何意义:
var xmlResponse = await File.ReadAllTextAsync(xmlResponsePath);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xmlResponse));
using var xmlReader = new XmlTextReader(stream) {XmlResolver = null};
var serializer = new XmlSerializer(typeof(WhoisServiceExternal.WhoisRecordV2));
var record = serializer.Deserialize(xmlReader) as WhoisServiceExternal.WhoisRecordV2;
并且 record
确实已正确创建并填充了其属性。但是, XML 并不是我真正想要反序列化的。我真正想要的是:
<nameServers>
<hostNames>
<Address>ns1.google.com</Address>
<Address>ns2.google.com</Address>
</hostNames>
</nameServers>
我的问题是:如何调整我的 F# 模型,以便 XML 的第二部分正确反序列化?
我觉得问这样一个微不足道的问题很傻,但我觉得我已经尝试了太多模型属性的配置,不同的属性和名称,但似乎没有任何效果 - Address
字段的集合不是正确反序列化。我要么看到一个空的 hostNames
集合,要么看到空白的 Address
属性。
我肯定遗漏了一些明显的东西。
更新:
感谢您回答费奥多尔。以下是我未修剪的 XML 和工作模型:
[<CLIMutable; XmlType("Address")>]
type Address =
{ [<XmlText>]
Address: string }
[<CLIMutable>]
type WhoisRecordNameServersV2 =
{ rawText: string
[<XmlArray("hostNames")>]
hostNames: Address [] }
<nameServers>
<rawText>ns1.google.com
ns3.google.com
ns2.google.com
ns4.google.com
</rawText>
<hostNames>
<Address>ns1.google.com</Address>
<Address>ns2.google.com</Address>
</hostNames>
<ips/>
</nameServers>
看来你这里的是:
<nameServers>
,其中
<hostNames>
,其中
<Address>
的数组,其中
- 纯文本
并且您想将其映射到:
WhoisRecordNameServersV2
,其中
hostNames
,其中
- 一个
AddressWrapper
的数组,其中
Address: string
看到水货了吗?对我来说,这看起来像是幸运的一对一映射:
<nameServers>
映射到 WhoisRecordNameServersV2
<hostNames>
映射到 hostNames
<Address>
映射到 AddressWrapper
<Address>
内的纯文本映射到 AddressWrapper
内的 Address
。
现在您需要做的就是正确注释类型和字段:
WhoisRecordNameServersV2
映射到<nameServers>
,所以需要XmlType("nameServers")
hostNames
映射到 <hostNames>
并包含一个数组,所以它需要 XmlArray("hostNames")
AddressWrapper
映射到<Address>
,所以需要XmlType("Address")
AddressWrapper
内的Address
映射到<Address>
内的纯文本,所以需要XmlText
.
综合起来:
[<CLIMutable; XmlType("Address")>]
type AddressWrapper =
{ [<XmlText>] Address: string }
[<CLIMutable; XmlType("nameServers")>]
type WhoisRecordNameServersV2 =
{ [<XmlArray("hostNames")>] hostNames: AddressWrapper [] }
我有将某些模型从 C# 迁移到 F# 的不寻常要求,老实说,我在 XML 反序列化方面遇到了一些困难。这是我到目前为止所拥有的。以下 XML + 反序列化 C# 代码 + F# 模型的配对非常有效:
<nameServers>
<hostNames>
<AddressWrapper>
<Address>www.blah1.com</Address>
</AddressWrapper>
<AddressWrapper>
<Address>www.blah2.com</Address>
</AddressWrapper>
</hostNames>
</nameServers>
[<CLIMutable>]
[<XmlTypeAttribute("AddressWrapper")>]
type AddressWrapper =
{ [<XmlElement("Address")>]
Address: string }
[<CLIMutable>]
type WhoisRecordNameServersV2 =
{ [<XmlArray("hostNames")>]
hostNames: AddressWrapper [] }
以下只是随笔,不要对代码附加任何意义:
var xmlResponse = await File.ReadAllTextAsync(xmlResponsePath);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(xmlResponse));
using var xmlReader = new XmlTextReader(stream) {XmlResolver = null};
var serializer = new XmlSerializer(typeof(WhoisServiceExternal.WhoisRecordV2));
var record = serializer.Deserialize(xmlReader) as WhoisServiceExternal.WhoisRecordV2;
并且 record
确实已正确创建并填充了其属性。但是, XML 并不是我真正想要反序列化的。我真正想要的是:
<nameServers>
<hostNames>
<Address>ns1.google.com</Address>
<Address>ns2.google.com</Address>
</hostNames>
</nameServers>
我的问题是:如何调整我的 F# 模型,以便 XML 的第二部分正确反序列化?
我觉得问这样一个微不足道的问题很傻,但我觉得我已经尝试了太多模型属性的配置,不同的属性和名称,但似乎没有任何效果 - Address
字段的集合不是正确反序列化。我要么看到一个空的 hostNames
集合,要么看到空白的 Address
属性。
我肯定遗漏了一些明显的东西。
更新:
感谢您回答费奥多尔。以下是我未修剪的 XML 和工作模型:
[<CLIMutable; XmlType("Address")>]
type Address =
{ [<XmlText>]
Address: string }
[<CLIMutable>]
type WhoisRecordNameServersV2 =
{ rawText: string
[<XmlArray("hostNames")>]
hostNames: Address [] }
<nameServers>
<rawText>ns1.google.com
ns3.google.com
ns2.google.com
ns4.google.com
</rawText>
<hostNames>
<Address>ns1.google.com</Address>
<Address>ns2.google.com</Address>
</hostNames>
<ips/>
</nameServers>
看来你这里的是:
<nameServers>
,其中<hostNames>
,其中<Address>
的数组,其中- 纯文本
并且您想将其映射到:
WhoisRecordNameServersV2
,其中hostNames
,其中- 一个
AddressWrapper
的数组,其中 Address: string
看到水货了吗?对我来说,这看起来像是幸运的一对一映射:
<nameServers>
映射到WhoisRecordNameServersV2
<hostNames>
映射到hostNames
<Address>
映射到AddressWrapper
<Address>
内的纯文本映射到AddressWrapper
内的Address
。
现在您需要做的就是正确注释类型和字段:
WhoisRecordNameServersV2
映射到<nameServers>
,所以需要XmlType("nameServers")
hostNames
映射到<hostNames>
并包含一个数组,所以它需要XmlArray("hostNames")
AddressWrapper
映射到<Address>
,所以需要XmlType("Address")
Address
映射到<Address>
内的纯文本,所以需要XmlText
.
AddressWrapper
内的综合起来:
[<CLIMutable; XmlType("Address")>]
type AddressWrapper =
{ [<XmlText>] Address: string }
[<CLIMutable; XmlType("nameServers")>]
type WhoisRecordNameServersV2 =
{ [<XmlArray("hostNames")>] hostNames: AddressWrapper [] }