使用因属性而异的元素反序列化 XML

Posted

技术标签:

【中文标题】使用因属性而异的元素反序列化 XML【英文标题】:Deserialize XML with elements that differ by attribute 【发布时间】:2021-11-12 03:44:42 【问题描述】:

我有这个 XML 的 sn-p(实际上是 XBRL,但那是基于 XML)

<xbrl>
<context id="Context_Duration" />
<context id="Context_Instant_Begin" />
<context id="Context_Instant_End" />    
<ConstructionDepotSpecification>
    <ConstructionDepotLabel contextRef="Context_Duration">depot</ConstructionDepotLabel>
    <ConstructionDepotBalance contextRef="Context_Instant_Begin">45000</ConstructionDepotBalance>
    <ConstructionDepotBalance contextRef="Context_Instant_End">42000</ConstructionDepotBalance>
</ConstructionDepotSpecification> 
</xbrl>

(为清楚起见,删除了其他内容和 xml 命名空间声明)

我想将其反序列化为一个类,但我不确定如何处理 ConstructionDepotBalance 元素。如果我定义一个属性 ConstructionDepotBalance ,它将只取第一个元素的值,所以我认为我应该创建两个属性,一个用于开始值,一个用于结束值。 所以这个类应该是这样的

[XmlRoot(ElementName = "xbrl")]
public partial class Taxonomy

    [XmlElement]
    public List<ConstructionDepotSpecification> ConstructionDepotSpecification  get; set; 


public partial class ConstructionDepotSpecification 

    public string ConstructionDepotLabel  get; set; 

    public long? ConstructionDepotBalanceBegin  get; set; 
    public long? ConstructionDepotBalanceEnd  get; set; 

因此,具有 Context_Instant_Begin 属性的元素应反序列化为 ConstructionDepotBalanceBegin,而具有 Context_Instant_End 属性的其他元素应反序列化为 ConstructionDepotBalanceEnd。

这有可能实现吗?我应该为此使用 IXmlSerializable 实现吗?

【问题讨论】:

【参考方案1】:

我的第一种方法:

您可以先解析 XML-String 并替换

[ConstructionDepotBalance contextRef="Context_Instant_Begin"]

[ConstructionDepotBalanceBegin]

(与ConstructionDepotBalanceEnd相同)。

在第二步中,您将反序列化 XML 字符串。

【讨论】:

谢谢,这可能适用于简化的示例,但真正的 XML 更复杂,所以我不想那样做 也许这可以帮助您在第一步解析属性。在第二步(如果需要),您可以将结果转换为您想要的数据结构。 ***.com/questions/14245846/…【参考方案2】:

我对 IXmlSerializable 接口进行了一些实验,并提出了这个实现:

public void ReadXml(XmlReader reader)

    reader.ReadStartElement();
    while (!reader.EOF)
    
        var ctx = reader.GetAttribute("contextRef");
        if (ctx == "Context_Duration")
        
            string propName = reader.Name;
            var propInfo = GetType().GetProperty(propName);
            Type propType = propInfo.PropertyType;
            if (propType.GenericTypeArguments.Length > 0)
            
                propType = propType.GenericTypeArguments[0];
            
            var value = reader.ReadElementContentAs(propType, null);
            propInfo.SetValue(this, value);
        

        else if (ctx == "Context_Instant_Begin")
        
            string propName = reader.Name + "Begin";
            var propInfo = GetType().GetProperty(propName);
            var value = reader.ReadElementContentAsLong();
            propInfo.SetValue(this, value);
        

        else if (ctx == "Context_Instant_End")
        
            string propName = reader.Name + "End";
            var propInfo = GetType().GetProperty(propName);
            var value = reader.ReadElementContentAsLong();
            propInfo.SetValue(this, value);
        

        if (reader.NodeType == XmlNodeType.EndElement)
        
            reader.ReadEndElement();
            break;
        
    

不确定这是否是解决此问题的最佳解决方案,但目前它可以满足我的需求。

【讨论】:

以上是关于使用因属性而异的元素反序列化 XML的主要内容,如果未能解决你的问题,请参考以下文章

如何在c#中使用具有相同名称但不同属性和结构的元素反序列化XML

如何使用 Fluent Assertion 比较两个因属性而异的集合?

在 C# 中反序列化 XML 元素的存在以布尔

如何从动态对象中获取反序列化的 xml 属性

如何将 xml 元素值反序列化为 C# 类属性

C# xml 反序列化不会将子元素提取到列表中