C# 列表中有空元素的自定义XmlSerializer问题<&燃气轮机;
我有一个需要自定义序列化程序的类。有时我会在一个列表中使用这个类,我也想序列化它。其中一些元素将为空 我可以开始工作:C# 列表中有空元素的自定义XmlSerializer问题<&燃气轮机;,c#,xml-serialization,C#,Xml Serialization,我有一个需要自定义序列化程序的类。有时我会在一个列表中使用这个类,我也想序列化它。其中一些元素将为空 我可以开始工作: <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=
<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>
即,在元素1和2中创建了MyFrac对象,而不是null
我是否必须为列表子类创建一个自定义序列化程序来解决这个问题,或者我是否缺少在反序列化时获取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]);
}
}
公共类MyFrac:IXmlSerializable
{
公共字符串N;
公共字符串D;
公共重写字符串ToString()
{
返回N+“/”+D;
}
System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema()
{
返回null;
}
void IXmlSerializable.ReadXml(System.Xml.XmlReader)
{
if(reader.IsEmptyElement&&reader.NodeType!=XmlNodeType.EndElement)
{
reader.Read();
返回;
}
reader.ReadStartElement();
string sfrac=reader.ReadString();
尝试
{
var m=Regex.Match(sfrac,@“(\d+)/(\d+));
如果(!m.Success)
抛出新异常(sfrac+“格式不正确”);
N=m.结果(“$1”);
D=m.结果($2);
}
最后
{
reader.ReadEndElement();
}
}
void IXmlSerializable.WriteXml(System.Xml.XmlWriter-writer)
{
writer.WriteString(N+“/”+D);
}
}
公共类MyData
{
[XmlArrayItem(“Frac”)]
公开名单分数;
}
公共静态无效运行()
{
var data=新的MyData();
data.fracts=新列表();
添加(新的MyFrac{N=“1”,D=“2”});
data.Fractions.Add(空);
data.Fractions.Add(空);
添加(新的MyFrac{N=“3”,D=“6”});
var serializer=newxmlserializer(typeof(MyData));
StringBuilder sb=新的StringBuilder();
使用(var编写器=新的StringWriter(sb))
{
序列化程序。序列化(写入程序、数据);
}
//转储XML
Console.WriteLine(sb.ToString());
使用(var reader=newstringreader(sb.ToString()))
{
var data2=(MyData)序列化程序。反序列化(读取器);
Console.WriteLine(data2.fracts[0]);
Console.WriteLine(数据2.分数[1]);
Console.WriteLine(数据2.分数[2]);
Console.WriteLine(数据2.分数[3]);
}
}
出现问题的原因是,在反序列化过程中,XmlSerializer首先通过调用默认构造函数创建对象,然后调用ReadXml方法设置属性值,以便ReadXml无法取消对象创建。是否需要序列化null值才能在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);
}
}
}
然后,序列化/反序列化按您的需要工作:
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
产出:
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);
}
}
}
公共类MyCollection:Collection,IXmlSerializable其中T:class
{
公共XmlSchema GetSchema()
{
返回null;
}
公共void ReadXml(XmlReader)
{
var serializer=newxmlserializer(typeof(T));
var waspempty=reader.IsEmptyElement;
reader.Read();
如果(为空)
返回;
while(reader.NodeType!=XmlNodeType.EndElement)
{
if(读卡器ISemptyelment)
{
reader.Read();
Items.Add(空);
继续;
}
var item=(T)序列化程序。反序列化(读取器);
项目。添加(项目);
}
reader.ReadEndElement();
}
public void WriteXml(XmlWriter)
{
var serializer=newxmlserializer(typeof(T));
foreach(项目中的var myFrac)
{
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();
}
}
公共类MyData
{
公共真菌采集组分;
}
班级计划
{
静态void Main(字符串[]参数)
{
var data=新的MyData();
data.fracts=新的MyCollection();
添加(新的MyFrac{N=“1”,D=“2”});
data.Fractions.Add(空);
data.Fractions.Add(空);
添加(新的MyFrac{N=“3”,D=“6”});
var serializer=newxmlserializer(typeof(MyData));
StringBuilder sb=新的StringBuilder();
使用(var编写器=新的StringWriter(sb))
{
序列化程序。序列化(写入程序、数据);
}
//转储XML
Console.WriteLine(sb.ToString());
Trace.WriteLine(sb.ToString());
使用(var reader=newstringreader(sb.ToString()))
{
var data2=(MyData)序列化程序。反序列化(读取器);
foreach(数据中的var分数2.分数)
{
var output=fraction==null?“null”:fraction.ToString();
控制台写入线(输出);
Trace.WriteLine(输出);
}
}
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
输出数据:
1/2
null
null
3/6
我想这就是你想要的。空列表元素有意义。所以我需要这些XML文件并还原。是的,谢谢。我希望我忽略了其他一些XmlSerializer属性,我可以应用这些属性来让标准列表序列化程序“做正确的事情”,因为这似乎是一个非常常见的障碍。再次感谢。
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 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>