XmlSerializer 按属性过滤
XmlSerializer filter by attribute
使用 XmlSerializer 将 XML 反序列化为实体 class 时,是否可以按属性进行过滤?例如,假设我有一个可以是 "a" 类型或 "b" 类型的项目。我想反序列化所有项目,但只反序列化 "a".
类型的项目
我需要这个,因为我的实际情况是我们的端点接收到非常大的 XMLs(有些可以超过 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 问题也不是过早优化。我们已经确定我们需要通过分析等来提高这方面的性能。同样过滤掉值 post-反序列化也无济于事 - 到那时分配已经完成并且必须进行垃圾收集。
这可以通过我们自己处理反序列化过程来实现(至少对于根 class)
请提醒您,您提供的 XML 内容不足以进行 运行 单元测试,因此这是一个非常基本的实现,但是应该可以直接或通过只是在这里和那里稍微调整一下。
首先,我们将 Item class XML 序列化属性更改为 root。 "Why" 很快就会得到答复。
[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>
现在是根 class,它现在明确地实现了 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().
剩下的..就是测试了。
使用 XmlSerializer 将 XML 反序列化为实体 class 时,是否可以按属性进行过滤?例如,假设我有一个可以是 "a" 类型或 "b" 类型的项目。我想反序列化所有项目,但只反序列化 "a".
类型的项目我需要这个,因为我的实际情况是我们的端点接收到非常大的 XMLs(有些可以超过 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 问题也不是过早优化。我们已经确定我们需要通过分析等来提高这方面的性能。同样过滤掉值 post-反序列化也无济于事 - 到那时分配已经完成并且必须进行垃圾收集。
这可以通过我们自己处理反序列化过程来实现(至少对于根 class)
请提醒您,您提供的 XML 内容不足以进行 运行 单元测试,因此这是一个非常基本的实现,但是应该可以直接或通过只是在这里和那里稍微调整一下。
首先,我们将 Item class XML 序列化属性更改为 root。 "Why" 很快就会得到答复。
[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>
现在是根 class,它现在明确地实现了 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().
剩下的..就是测试了。