自定义 XmlSerializer 问题,列表中<> 中的空元素
本文关键字:元素 自定义 问题 列表 XmlSerializer | 更新日期: 2023-09-27 18:35:25
我有一个类,我想要一个自定义序列化程序。我有时会在列表中使用这个类,我也想序列化它。其中一些元素将为空。
我可以让序列化工作:
<MyData xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Fractions>
<Frac>1/2</Frac>
<Frac xsi:nil="true" />
<Frac xsi:nil="true" />
<Frac>3/6</Frac>
</Fractions>
</MyData>
但是,当如上所述存在空元素时,反序列化不起作用。List<> 序列化程序似乎正在为元素调用 ReadXml(),而不是仅在列表中创建一个空元素。
当我运行我的示例时,反序列化版本是:
1/2
/
/
3/6
即在元素 1 和 2 中创建了一个 MyFrac 对象而不是 null。
我是否必须为 List 子类创建自定义序列化程序来解决此问题,或者我是否缺少其他方法来获取反序列化的 null 元素?如果是自定义序列化程序,有什么最佳方法/代码吗?
我在下面有一个完整的示例,显示了我当前的实现。
public class MyFrac : IXmlSerializable
{
public string N;
public string D;
public override string ToString()
{
return N + "/" + D;
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
return null;
}
void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)
{
if (reader.IsEmptyElement && reader.NodeType != XmlNodeType.EndElement)
{
reader.Read();
return;
}
reader.ReadStartElement();
string sfrac = reader.ReadString();
try
{
var m = Regex.Match(sfrac, @"('d+)/('d+)");
if (!m.Success)
throw new Exception(sfrac + " was not in the correct format");
N = m.Result("$1");
D = m.Result("$2");
}
finally
{
reader.ReadEndElement();
}
}
void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteString(N + "/" + D);
}
}
public class MyData
{
[XmlArrayItem("Frac")]
public List<MyFrac> Fractions;
}
public static void Run()
{
var data = new MyData();
data.Fractions = new List<MyFrac>();
data.Fractions.Add(new MyFrac { N = "1", D = "2" });
data.Fractions.Add(null);
data.Fractions.Add(null);
data.Fractions.Add(new MyFrac { N = "3", D = "6" });
var serializer = new XmlSerializer(typeof(MyData));
StringBuilder sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
serializer.Serialize(writer, data);
}
// Dump XML
Console.WriteLine(sb.ToString());
using (var reader = new StringReader(sb.ToString()))
{
var data2 = (MyData)serializer.Deserialize(reader);
Console.WriteLine(data2.Fractions[0]);
Console.WriteLine(data2.Fractions[1]);
Console.WriteLine(data2.Fractions[2]);
Console.WriteLine(data2.Fractions[3]);
}
}
由于在反序列化期间出现问题 XmlSerializer 首先通过调用默认构造函数创建一个对象,然后调用 ReadXml 方法来设置属性值,以便 ReadXml 无法取消对象创建。是否需要序列化空值才能在 xml 中查看它?我的意思是,您可以通过使用非列表集合来避免这种情况。例如,创建自定义集合:
public class MyCollection : System.Collections.ObjectModel.Collection<MyFrac>
{
protected override void InsertItem(int index, MyFrac item)
{
if(item == null) return;
base.InsertItem(index, item);
}
protected override void SetItem(int index, MyFrac item)
{
if(item == null)
{
base.RemoveAt(index);
}
else
{
base.SetItem(index, item);
}
}
}
在 MyData 类中使用它:
public class MyData
{
[XmlArrayItem("Frac")]
public MyCollection Fractions;
}
然后序列化/反序列化按预期工作:
public static void Main(string[] args)
{
var data = new MyData();
data.Fractions = new MyCollection();
data.Fractions.Add(new MyFrac { N = "1", D = "2" });
data.Fractions.Add(null);
data.Fractions.Add(null);
data.Fractions.Add(new MyFrac { N = "3", D = "6" });
var serializer = new XmlSerializer(typeof(MyData));
StringBuilder sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
serializer.Serialize(writer, data);
}
// Dump XML
Console.WriteLine(sb.ToString());
using (var reader = new StringReader(sb.ToString()))
{
var data2 = (MyData)serializer.Deserialize(reader);
foreach (var element in data2.Fractions) {
Console.WriteLine(element);
}
}
Console.ReadLine();
}
序列化的 xml:
<?xml version="1.0" encoding="utf-16"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Fractions>
<Frac>1/2</Frac>
<Frac>3/6</Frac>
</Fractions>
</MyData>
输出:
1/2
3/6
更新
好的,您需要一个具有自定义序列化规则的集合。让我们实现它:
public class MyCollection<T> : Collection<T>, IXmlSerializable where T: class
{
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
var serializer = new XmlSerializer(typeof(T));
var wasEmpty = reader.IsEmptyElement;
reader.Read();
if (wasEmpty)
return;
while (reader.NodeType != XmlNodeType.EndElement)
{
if (reader.IsEmptyElement)
{
reader.Read();
Items.Add(null);
continue;
}
var item = (T)serializer.Deserialize(reader);
Items.Add(item);
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter writer)
{
var serializer = new XmlSerializer(typeof (T));
foreach (var myFrac in Items)
{
serializer.Serialize(writer, myFrac);
}
}
}
用法:
public class MyData
{
public MyCollection<MyFrac> Fractions;
}
class Program
{
static void Main(string[] args)
{
var data = new MyData();
data.Fractions = new MyCollection<MyFrac>();
data.Fractions.Add(new MyFrac { N = "1", D = "2" });
data.Fractions.Add(null);
data.Fractions.Add(null);
data.Fractions.Add(new MyFrac { N = "3", D = "6" });
var serializer = new XmlSerializer(typeof(MyData));
StringBuilder sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
serializer.Serialize(writer, data);
}
// Dump XML
Console.WriteLine(sb.ToString());
Trace.WriteLine(sb.ToString());
using (var reader = new StringReader(sb.ToString()))
{
var data2 = (MyData)serializer.Deserialize(reader);
foreach (var fraction in data2.Fractions)
{
var output = fraction == null ? "null" : fraction.ToString();
Console.WriteLine(output);
Trace.WriteLine(output);
}
}
Console.ReadLine();
}
}
输出 xml:
<?xml version="1.0" encoding="utf-16"?>
<MyData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Fractions>
<MyFrac>1/2</MyFrac>
<MyFrac xsi:nil="true" />
<MyFrac xsi:nil="true" />
<MyFrac>3/6</MyFrac>
</Fractions>
</MyData>
输出数据:
1/2
零
零
3/6
我想这就是你想要的。