我正在使用不导入缩写的空xml元素的遗留应用程序。例如:
坏空:
<foo />
好空:
<foo></foo>
我知道实现这一目标的解决方案,我现在将介绍:
public class XmlTextWriterFull : XmlTextWriter
{
public XmlTextWriterFull(Stream stream, Encoding enc) : base(stream, enc)
{
}
public XmlTextWriterFull(String str, Encoding enc) : base(str, enc)
{
}
public override void WriteEndElement()
{
base.WriteFullEndElement();
}
}
和客户端代码:
var x_settings = new XmlWriterSettings();
x_settings.NewLineChars = Environment.NewLine;
x_settings.NewLineOnAttributes = true;
x_settings.NewLineHandling = NewLineHandling.Replace;
x_settings.CloseOutput = true;
x_settings.Indent = true;
x_settings.NewLineOnAttributes = true;
//var memOut = new MemoryStream();
var writer = new XmlTextWriterFull(outputFilename, Encoding.UTF8); //Or the encoding of your choice
var x_serial = new XmlSerializer(typeof(YOUR_OBJECT_TYPE));
x_serial.Serialize(writer, YOUR_OBJECT_INSTANCE);
writer.Close();
但是,如果您仔细观察,XmlWriterSettings
从未在客户端代码中使用过。因此xml输出格式非常复杂。我的问题是:如何调整上述代码以接受XmlWriterSettings
?
使用工厂创建方法和密封/内部/抽象类会使实现覆盖变得困难。
我会接受另一种解决方案,我与上述解决方案没有结合。
第1步:在解决方案中创建以下类:
public class XmlTextWriterFull : XmlTextWriter
{
public XmlTextWriterFull(TextWriter sink) : base(sink)
{
Formatting = Formatting.Indented;
}
public override void WriteEndElement()
{
base.WriteFullEndElement();
}
}
步骤2:添加以下客户端代码。确保将YOUR_OBJECT_TYPE和YOUR_OBJECT_INSTANCE替换为您正在使用的类和实例:
TextWriter streamWriter = new StreamWriter(outputFilename);
var writer = new XmlTextWriterFull(streamWriter);
var x_serial = new XmlSerializer(typeof (YOUR_OBJECT_TYPE));
x_serial.Serialize(writer, YOUR_OBJECT_INSTANCE);
writer.Close();
上面的解决方法将产生以下空xml元素格式:
<foo>
</foo>
此解决方法的问题是它添加换行符(请注意元素位于不同的行上)。这可能是您可以接受的,但会导致我的遗留应用程序出现问题。
答案 0 :(得分:5)
这个怎么样。
从http://www.tkachenko.com/blog/archives/000585.html抓取了令人敬畏的XmlWrappingWriter
类(为了简洁起见,我省略了代码)。
有了这个,我们可以创建一个子类,如下所示(非常类似于原始子类):
public class XmlTextWriterFull2 : XmlWrappingWriter
{
public XmlTextWriterFull2(XmlWriter baseWriter)
: base(baseWriter)
{
}
public override void WriteEndElement()
{
base.WriteFullEndElement();
}
}
然后可以像这样调用它(再次非常相似):
var x_settings = new XmlWriterSettings();
x_settings.NewLineChars = Environment.NewLine;
x_settings.NewLineOnAttributes = true;
x_settings.NewLineHandling = NewLineHandling.None;
x_settings.CloseOutput = true;
x_settings.Indent = true;
x_settings.NewLineOnAttributes = true;
using (XmlWriter writer = XmlWriter.Create(outputFilename, x_settings))
{
using (XmlTextWriterFull2 xmlTextWriterFull = new XmlTextWriterFull2(writer))
{
var x_serial = new XmlSerializer(typeof(YOUR_OBJECT_TYPE));
x_serial.Serialize(xmlTextWriterFull, YOUR_OBJECT_INSTANCE);
}
}
就我而言,一个先前被渲染为
的元素<Foo>
</Foo>
成了
<Foo></Foo>
正如你在问题中提到的那样,这实际上是一个非常棘手的问题,因为所有东西都被密封/内部等等,因此覆盖相当困难。我认为我最大的问题是尝试XmlWriter
接受XmlWriterSettings
:除了这种方法,我无法让原始的XmlTextWriterFull
尊重给定的XmlWriterSettings
MSDN说明了这种方法:
XmlWriter.Create(XmlWriter, XmlWriterSettings)
可用于将XmlWriterSettings
应用于XmlWriter
。我不能像我想的那样工作(缩进从未工作过,例如),并且在反编译代码时,似乎没有所有设置都与这个特定方法一起使用,因此为什么我的调用代码只是传入outputFile
(某种流也会起作用)。
答案 1 :(得分:2)
您在问题中提供的变通解决方案会增加额外的换行符(启用缩进时),因为我们告诉作者将此元素视为具有子级。
以下是我修改您的变通方法以动态操作缩进的方法,以避免这些额外的换行符。
public class XmlTextWriterFull : XmlTextWriter
{
public XmlTextWriterFull(TextWriter sink)
: base(sink)
{
Formatting = Formatting.Indented;
}
private bool inElement = false;
public override void WriteStartElement(string prefix, string localName, string ns)
{
base.WriteStartElement(prefix, localName, ns);
// Remember that we're in the process of defining an element.
// As soon as a child element is closed, this flag won't be true anymore and we'll know to avoid messing with the indenting.
this.inElement = true;
}
public override void WriteEndElement()
{
if (!this.inElement)
{
// The element being closed has child elements, so we should just let the writer use it's default behavior.
base.WriteEndElement();
}
else
{
// It looks like the element doesn't have children, and we want to avoid emitting a self-closing tag.
// First, let's temporarily disable any indenting, then force the full closing element tag.
var prevFormat = this.Formatting;
this.Formatting = Formatting.None;
base.WriteFullEndElement();
this.Formatting = prevFormat;
this.inElement = false;
}
}
}
答案 2 :(得分:0)
另一种选择。
public class XmlCustomTextWriter : XmlTextWriter
{
private TextWriter _tw = null;
public XmlCustomTextWriter(TextWriter sink)
: base(sink)
{
_tw = sink;
Formatting = Formatting.Indented;
Indentation = 0;
}
public void OutputElement(string name, string value)
{
WriteStartElement(name);
string nl = _tw.NewLine;
_tw.NewLine = "";
WriteString(value);
WriteFullEndElement();
_tw.NewLine = nl;
}
}
答案 3 :(得分:0)
将此留在这里以防有人需要它;因为上面的答案都没有为我解决,或者看起来有点矫枉过正。
FileStream fs = new FileStream("file.xml", FileMode.Create);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter w = XmlWriter.Create(fs, settings);
w.WriteStartDocument();
w.WriteStartElement("tag1");
w.WriteStartElement("tag2");
w.WriteAttributeString("attr1", "val1");
w.WriteAttributeString("attr2", "val2");
w.WriteFullEndElement();
w.WriteEndElement();
w.WriteEndDocument();
w.Flush();
fs.Close();
诀窍是设置 XmlWriterSettings.Indent = true 并将其添加到 XmlWriter 。< / p>
或者您也可以使用
w.Formatting = Formatting.Indented;
而不是添加XmlWriterSettings。
答案 4 :(得分:0)
以下代码片段在同一行上强制打印结束标记(对不起vb版本,应该很容易使用C#重写它):
Imports System.Xml
Imports System.IO
Public Class CustomXmlTextWriter
Inherits XmlTextWriter
Public Sub New(ByRef baseWriter As TextWriter)
MyBase.New(baseWriter)
Formatting = Xml.Formatting.Indented
End Sub
Public Overrides Sub WriteEndElement()
If Not (Me.WriteState = Xml.WriteState.Element) Then
MyBase.WriteEndElement()
Else
Formatting = Xml.Formatting.None
MyBase.WriteFullEndElement()
Formatting = Xml.Formatting.Indented
End If
End Sub
End Class