我想以紧凑的格式将xml文档写入磁盘。为此,我使用了网络框架方法XmlDictionaryWriter.CreateBinaryWriter(Stream stream,IXmlDictionary dictionary)
此方法编写自定义紧凑二进制xml表示形式,稍后可由XmlDictionaryWriter.CreateBinaryReader
读取。该方法接受可以包含公共字符串的XmlDictionary
,以便每次都不必在输出中打印这些字符串。字典索引将打印在文件中,而不是字符串。 CreateBinaryReader
稍后可以使用相同的字典来反转该过程。
然而,我通过的词典显然没有使用。请考虑以下代码:
using System.IO;
using System.Xml;
using System.Xml.Linq;
class Program
{
public static void Main()
{
XmlDictionary dict = new XmlDictionary();
dict.Add("myLongRoot");
dict.Add("myLongAttribute");
dict.Add("myLongValue");
dict.Add("myLongChild");
dict.Add("myLongText");
XDocument xdoc = new XDocument();
xdoc.Add(new XElement("myLongRoot",
new XAttribute("myLongAttribute", "myLongValue"),
new XElement("myLongChild", "myLongText"),
new XElement("myLongChild", "myLongText"),
new XElement("myLongChild", "myLongText")
));
using (Stream stream = File.Create("binaryXml.txt"))
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict))
{
xdoc.WriteTo(writer);
}
}
}
生成的输出为此(二进制控制字符未显示)
@
myLongRootmyLongAttribute˜myLongValue@myLongChild™
myLongText@myLongChild™
myLongText@myLongChild™
myLongText
显然XmlDictionary尚未使用过。所有字符串都在输出中完整显示,甚至多次。
这不仅限于XDocument。在上面的最小例子中,我使用了一个XDocument来演示这个问题,但最初我在使用XmlDictionaryWriter和DataContractSerializer时偶然发现了这个问题,因为它是常用的。结果是一样的:
[Serializable]
public class myLongChild
{
public double myLongText = 0;
}
...
using (Stream stream = File.Create("binaryXml.txt"))
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream, dict))
{
var dcs = new DataContractSerializer(typeof(myLongChild));
dcs.WriteObject(writer, new myLongChild());
}
结果输出没有使用我的XmlDictionary。
如何让XmlDictionaryWriter使用上传的XmlDictionary?
或者我误解了它是如何工作的?
使用DataContractSerializer方法,我尝试调试网络框架代码( visual studio / options / debugging / enable net。framework source stepping )。显然,Writer会尝试按预期查找字典中的每个字符串。但是line 356 of XmlbinaryWriter.cs中的查找失败,原因我不清楚。
我考虑过的替代方案:
XmlDictionaryWriter.CreatebinaryWriter存在重载,它也接受XmlBinaryWriterSession。然后,编写器将它遇到的任何新字符串添加到会话字典中。但是,我只想使用静态字典进行读写,这是事先已知的。
我可以将整个事物包装成GzipStream
并让压缩处理多个字符串实例。但是,这不会压缩每个字符串的第一个实例,而且整体上似乎是一种笨拙的解决方法。
答案 0 :(得分:3)
是的,存在误解。 XmlDictionaryWriter
主要用于对象的序列化,它是XmlWriter
的子类。 XDocument.WriteTo(XmlWriter something)
以XmlWriter
为参数。调用XmlDictionaryWriter.CreateBinaryWriter
将在内部创建System.Xml.XmlBinaryNodeWriter
的实例。这个类有两种方法用于"常规"写作:
// override of XmlWriter
public override void WriteStartElement(string prefix, string localName)
{
// plain old "xml" for me please
}
以及基于字典的方法:
// override of XmlDictionaryWriter
public override void WriteStartElement(string prefix, XmlDictionaryString localName)
{
// I will use dictionary to hash element names to get shorter output
}
如果您通过DataContractSerializer
序列化对象,则主要使用后者(注意其方法WriteObject
同时引用XmlDictionaryWriter
和XmlWriter
类型的参数),而{{1}只需要XDocument
。
关于你的问题 - 如果我是你,我自己做XmlWriter
:
XmlWriter
更新(根据您的评论)
如果您确实使用class CustomXmlWriter : XmlWriter
{
private readonly XmlDictionaryWriter _writer;
public CustomXmlWriter(XmlDictionaryWriter writer)
{
_writer = writer;
}
// override XmlWriter methods to use the dictionary-based approach instead
}
,则代码中的错误很少。
1)POC类必须用DataContractSerializer
和[DataContract]
属性进行修饰,序列化值应该是property而不是field;还将名称空间设置为空值,或者您也必须处理字典中的名称空间。像:
[DataMember]
2)也提供会话实例;对于null会话,字典编写器使用默认的(namespace XmlStuff {
[DataContract(Namespace = "")]
public class myLongChild
{
[DataMember]
public double myLongText { get; set; }
}
[DataContract(Namespace = "")]
public class myLongRoot
{
[DataMember]
public IList<myLongChild> Items { get; set; }
}
}
- )实现:
XmlWriter