C# 当xml具有缩进/换行符时,字典的自定义序列化失败
为了使字典序列化的XML更清晰,我编写了一个自定义类,它实现了C# 当xml具有缩进/换行符时,字典的自定义序列化失败,c#,xml,serialization,ixmlserializable,C#,Xml,Serialization,Ixmlserializable,为了使字典序列化的XML更清晰,我编写了一个自定义类,它实现了IXmlSerializable 我的自定义类定义如下: public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable { private const string XmlElementName = "MyData"; private const string XmlAt
IXmlSerializable
我的自定义类定义如下:
public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable
{
private const string XmlElementName = "MyData";
private const string XmlAttributeId = "Id";
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
reader.MoveToContent();
while (reader.Read())
{
if(reader.LocalName == XmlElementName)
{
var tag = reader.GetAttribute(XmlAttributeId);
var content = reader.ReadElementContentAsString();
this.Add(tag, content);
}
}
}
public void WriteXml(System.Xml.XmlWriter writer)
{
foreach (string key in this.Keys)
{
writer.WriteStartElement(XmlElementName);
writer.WriteAttributeString(XmlAttributeId, key);
writer.WriteString(this[key]);
writer.WriteEndElement();
}
}
}
它在调用ReadElementContentAsString
时抛出
如何修复我的代码
我可以使用以下方法重新设置问题:
var xml = @"<MyCollection xmlns=""http://schemas.datacontract.org/2004/07/MyProject"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><MyData Id=""1"">some content </MyData><MyData Id=""2"">some other content</MyData></MyCollection>";
var raw = Encoding.UTF8.GetBytes(xml);
var serializer = new DataContractSerializer(typeof(MyCollection));
using (var ms = new MemoryStream(raw))
{
var result = serializer.ReadObject(ms); // Exception throws here
}
var xml=@“一些内容一些其他内容”;
var raw=Encoding.UTF8.GetBytes(xml);
var serializer=新的DataContractSerializer(typeof(MyCollection));
使用(var ms=新内存流(原始))
{
var result=serializer.ReadObject(ms);//此处抛出异常
}
您的问题是将读卡器定位在下一个节点的开头,而不是当前节点的结尾。然后,对的后续无条件调用将使用下一个节点。当该节点为空白时,不会造成任何伤害,但当该节点为元素时,将跳过该元素
以下版本的MyCollection
修复了此问题:
public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable
{
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
using (var subReader = reader.ReadSubtree())
{
XmlKeyValueListHelper.ReadKeyValueXml(subReader, this);
}
// Consume the EndElement also (or move past the current element if reader.IsEmptyElement).
reader.Read();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
XmlKeyValueListHelper.WriteKeyValueXml(writer, this);
}
}
public static class XmlKeyValueListHelper
{
private const string XmlElementName = "MyData";
private const string XmlAttributeId = "Id";
public static void WriteKeyValueXml(System.Xml.XmlWriter writer, ICollection<KeyValuePair<string, string>> collection)
{
foreach (var pair in collection)
{
writer.WriteStartElement(XmlElementName);
writer.WriteAttributeString(XmlAttributeId, pair.Key);
writer.WriteString(pair.Value);
writer.WriteEndElement();
}
}
public static void ReadKeyValueXml(System.Xml.XmlReader reader, ICollection<KeyValuePair<string, string>> collection)
{
if (reader.IsEmptyElement)
{
reader.Read();
return;
}
reader.ReadStartElement(); // Advance to the first sub element of the list element.
while (reader.NodeType != XmlNodeType.EndElement)
{
if (reader.NodeType == XmlNodeType.Element && reader.LocalName == XmlElementName)
{
var tag = reader.GetAttribute(XmlAttributeId);
string content;
if (reader.IsEmptyElement)
{
content = string.Empty;
// Move past the end of item element
reader.Read();
}
else
{
// Read content and move past the end of item element
content = reader.ReadElementContentAsString();
}
collection.Add(new KeyValuePair<string, string>(tag, content));
}
else
{
// For instance a comment.
reader.Skip();
}
}
// Move past the end of the list element
reader.ReadEndElement();
}
}
公共类MyCollection:System.Collections.Generic.Dictionary,IXmlSerializable
{
公共XmlSchema GetSchema()
{
返回null;
}
公共void ReadXml(XmlReader)
{
使用(var subReader=reader.ReadSubtree())
{
ReadKeyValueXml(子读取器,this);
}
//同时使用EndElement(如果reader.IsEmptyElement,则移动当前元素)。
reader.Read();
}
public void WriteXml(System.Xml.XmlWriter)
{
WriteKeyValueXml(writer,this);
}
}
公共静态类XmlKeyValueListHelper
{
私有常量字符串xmlementname=“MyData”;
私有常量字符串XmlAttributeId=“Id”;
公共静态void WriteKeyValueXml(System.Xml.XmlWriter writer,ICollection集合)
{
foreach(集合中的变量对)
{
writer.writeStarteElement(XmlElementName);
WriteAttributeString(XmlAttributeId,pair.Key);
writer.WriteString(pair.Value);
writer.writeedelement();
}
}
公共静态void ReadKeyValueXml(System.Xml.XmlReader阅读器,ICollection集合)
{
if(读卡器ISemptyelment)
{
reader.Read();
返回;
}
reader.ReadStartElement();//前进到list元素的第一个子元素。
while(reader.NodeType!=XmlNodeType.EndElement)
{
if(reader.NodeType==XmlNodeType.Element&&reader.LocalName==XmlElementName)
{
var tag=reader.GetAttribute(XmlAttributeId);
字符串内容;
if(读卡器ISemptyelment)
{
content=string.Empty;
//移动到项目结束元素之后
reader.Read();
}
其他的
{
//读取内容并移过项目结束元素
content=reader.ReadElementContentAsString();
}
添加(新的KeyValuePair(标记、内容));
}
其他的
{
//比如一条评论。
reader.Skip();
}
}
//移动到列表元素的末尾
reader.ReadEndElement();
}
}
一些注意事项:
- 通过使用,我可以确保
的读取不会超过ReadXml()
元素的末尾,从而损坏未来的元素——在实现MyCollection
时很容易犯错误IXmlSerializable
- 通过检查
我将忽略意外类型的节点,例如注释reader.NodeType==XmlNodeType.Element&&reader.LocalName==xmlementName
正在工作。如果将代码更改为:
if(reader.LocalName==xmlementname&&reader.NodeType==XmlNodeType.Element)
。当您在结束标签上时,您似乎正在尝试阅读元素内容。谢谢,它工作起来很有魅力。使用XmlReader并不明显:(
System.InvalidOperationException: The ReadElementContentAsString method is not supported on node type EndElement
var xml = @"<MyCollection xmlns=""http://schemas.datacontract.org/2004/07/MyProject"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance""><MyData Id=""1"">some content </MyData><MyData Id=""2"">some other content</MyData></MyCollection>";
var raw = Encoding.UTF8.GetBytes(xml);
var serializer = new DataContractSerializer(typeof(MyCollection));
using (var ms = new MemoryStream(raw))
{
var result = serializer.ReadObject(ms); // Exception throws here
}
public class MyCollection : System.Collections.Generic.Dictionary<string, string>, IXmlSerializable
{
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
using (var subReader = reader.ReadSubtree())
{
XmlKeyValueListHelper.ReadKeyValueXml(subReader, this);
}
// Consume the EndElement also (or move past the current element if reader.IsEmptyElement).
reader.Read();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
XmlKeyValueListHelper.WriteKeyValueXml(writer, this);
}
}
public static class XmlKeyValueListHelper
{
private const string XmlElementName = "MyData";
private const string XmlAttributeId = "Id";
public static void WriteKeyValueXml(System.Xml.XmlWriter writer, ICollection<KeyValuePair<string, string>> collection)
{
foreach (var pair in collection)
{
writer.WriteStartElement(XmlElementName);
writer.WriteAttributeString(XmlAttributeId, pair.Key);
writer.WriteString(pair.Value);
writer.WriteEndElement();
}
}
public static void ReadKeyValueXml(System.Xml.XmlReader reader, ICollection<KeyValuePair<string, string>> collection)
{
if (reader.IsEmptyElement)
{
reader.Read();
return;
}
reader.ReadStartElement(); // Advance to the first sub element of the list element.
while (reader.NodeType != XmlNodeType.EndElement)
{
if (reader.NodeType == XmlNodeType.Element && reader.LocalName == XmlElementName)
{
var tag = reader.GetAttribute(XmlAttributeId);
string content;
if (reader.IsEmptyElement)
{
content = string.Empty;
// Move past the end of item element
reader.Read();
}
else
{
// Read content and move past the end of item element
content = reader.ReadElementContentAsString();
}
collection.Add(new KeyValuePair<string, string>(tag, content));
}
else
{
// For instance a comment.
reader.Skip();
}
}
// Move past the end of the list element
reader.ReadEndElement();
}
}