【问题标题】:XmlSerializer filter by attributeXmlSerializer 按属性过滤
【发布时间】:2020-04-24 10:08:52
【问题描述】:

使用 XmlSerializer 将 XML 反序列化为实体类时,是否可以按属性进行过滤?例如,假设我有一个可以是“a”类型或“b”类型的项目。我想反序列化所有项目,但只有“a”类型的项目。

我需要这个,因为我的实际情况是我们的端点接收到非常大的 XML(有些可能超过 100MB),其中包含数十万个 <item> 类型的标签,但我只需要其中一些 - “a”类型的标签”。我想避免分配其余部分(包括它们的子标签,这些标签不少)。

示例 XML:

<root>
  <item type="a"/>
  <item type="a"/>
  <item type="b"/>
  <item type="c"/>
</root>

实体:

[XmlRoot("root")]
public class Root {
    [XmlElement("item")]
    public Item[] Items { get; set; }
}
public class Item  {
    [XmlAttribute("type")]
    [DeserializeIfValueIs("a")] // <-- Is there something like this?
    public string Type { get; set; }
}

代码:

var serializer = new XmlSerializer(typeof(Root));
var dto = (Root) serializer.Deserialize(XmlReader.Create("input.xml"));
// Show the results - {"Items":[{"Type":"a"},{"Type":"a"},{"Type":"b"},{"Type":"c"}]}
Console.WriteLine(JsonConvert.SerializeObject(dto));

如何让它只为“a”类型的项目分配对象?

必须注意:这既不是 XY 问题,也不是过早优化。我们已经确定我们需要通过分析等来提高性能。同样过滤掉反序列化后的值也无济于事 - 到那时分配已经完成并且必须进行垃圾收集。

【问题讨论】:

    标签: c# xmlserializer


    【解决方案1】:

    这可以通过我们自己处理反序列化过程来实现(至少对于根类)

    请让我提醒您,您提供的 XML 内容不足以运行单元测试,所以这是一个非常基本的实现,但是应该可以直接为您工作,或者只是在这里和那里稍微调整一下。

    首先,我们将 Item 类 XML 序列化属性更改为 root。 “为什么”很快就会得到解答。

    [XmlRoot("item")]
    public class Item
    {
        [XmlAttribute("type")]
        public string Type { get; set; }
    
        [XmlElement("prop1")]
        public int Prop1 { get; set; }
    }
    

    我还添加了一个简单的整数属性来证明反序列化按预期工作。

    我还更改了 XML 内容以匹配新类型,以进行测试。

    <root>
      <item type="b">
        <prop1>5</prop1>
      </item>
      <item type="a">
        <prop1>5</prop1>
      </item>
      <item type="a">
        <prop1>5</prop1>
      </item>
      <item type="b">
        <prop1>5</prop1>
      </item>
      <item type="c">
        <prop1>5</prop1>
      </item>
    </root>
    

    现在是 Root 类,它现在显式实现 IXmlSerializable:

    [XmlRoot("root")]
    public class Root : IXmlSerializable
    {
        [XmlElement("item")]
        public Item[] Items { get; set; }
    
        // These two methods are not implemented for you need to deserialize only,
        // and because you haven't provided the schema for your XML content
        System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { throw new NotImplementedException(); }
        void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) { throw new NotImplementedException(); }
    
        void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
        {
            // The element is <root> when here for the first time.
    
            // Maintain a list to keep items with type "a"
            List<Item> typeAItems = new List<Item>();
    
            // Create a serializer for the type Item
            XmlSerializer deserializer = new XmlSerializer(typeof(Item));
    
            while (reader.Read())
            {
                // The code is self explanatory.
                // Skip() will help omitting unnecessary reads
                // if we are not interested in the Item
                if (reader.IsStartElement() && reader.Name == "item")
                {
                    if (reader.GetAttribute("type") == "a")
                    {
                        // This works, and deserializes the current node
                        // into an Item object. When the deserialization
                        // is completed, the reader is at the beginning
                        // of the next <Item> element
                        typeAItems.Add((Item)deserializer.Deserialize(reader));
                    }
                    else
                    {
                        // skip element with all its children
                        reader.Skip();
                    }
                }
                else
                {
                    // skip element with all its children
                    reader.Skip();
                }
            }
            Items = typeAItems.ToArray();
        }
    }
    

    反序列化逻辑保持不变,如 new XmlSerializer(typeof(Root)).Deserialize()。

    剩下的就是测试了。

    【讨论】:

      猜你喜欢
      • 2020-08-03
      • 1970-01-01
      • 2013-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多