如何在 C# 中使用 XmlDocument 停止空 XML 元素自闭?



在我被人们说XML解析器不应该关心元素是空的还是自闭的之前,我不能允许自闭的XML元素是有原因的。原因是我实际上使用的是SGML而不是XML,而且我正在使用的SGML DTD非常严格,不允许这样做。

我拥有的是几千个SGML文件,我需要在这些文件上运行XSLT。因此,我不得不暂时将SGML转换为XML,以便应用XSLT。然后,我编写了一个将它们转换回 SGML 的方法(实质上只是将 XML 声明替换为 SGML 声明,并写回任何其他实体声明,例如图形实体)。

我的问题是,在此转换回SGML之后,当我在SGML编辑器中打开文件时,文件不会解析,因为空元素已自行关闭。

有谁知道我在使用 XmlDocument 时如何阻止这种情况发生?

将 SGML 转换为 XML 并再次转换回来的方法如下所示

//converts the SGML file to XML – it’s during this conversion that the 
//empty elements get self-closed, i think
private XmlDocument convertToXML(TextReader reader)
{
// setup SgmlReader
Sgml.SgmlReader sgmlReader = new Sgml.SgmlReader();
//sgmlReader.DocType = "HTML";
sgmlReader.WhitespaceHandling = WhitespaceHandling.All;
sgmlReader.CaseFolding = Sgml.CaseFolding.ToLower;
sgmlReader.InputStream = reader;

// create document
XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;
doc.XmlResolver = null;
doc.Load(sgmlReader);
return doc;
}
// method to apply the XSLT stylesheet to the XML document
private void filterApplic(string applicFilter)
{
string stylesheet = getRequiredStylesheet(); // do this just once
if (stylesheet != "")
{
foreach (string file in FilesToConvert)
{
fileName = Path.GetFileName(file); //gets just the file name from the path
fileNameNoExt = Path.GetFileNameWithoutExtension(file);
string ext = Path.GetExtension(file);
if (ext == ".sgm")
{
try
{
publicIdentifier = getDoctype(file); // gets the sgml declaration
entitiesList = getEntitites(file); // gets the list of entities
TextReader tr = new StreamReader(file);
myDoc = convertToXML(tr);
myDoc.Save(outputFolder + "\temp.xml");
var myXslTrans = new XslCompiledTransform();
myXslTrans.Load(stylesheet);
myXslTrans.Transform(outputFolder + "\temp.xml", Path.Combine(outputFolder, fileNameNoExt +".xml"));
XmlDocument convertedDoc = new XmlDocument();
convertedDoc.Load(Path.Combine(outputFolder, fileNameNoExt + ".xml"));
convertToSGM(convertedDoc);
filesTransformed++;
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
}
}
else
{
MessageBox.Show("The stylesheet was retured empty. Cannot perform Applicability filter.");
return;
}

MessageBox.Show("Complete! " + filesTransformed.ToString() + " files filtered for " + applicFilter);
}

//convert files back to SGML
private void convertToSGM(XmlDocument myDoc)
{
using (var stringWriter = new StringWriter())
using (var xmlTextWriter = XmlWriter.Create(stringWriter, settings))
{
myDoc.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();

string xmltext = stringWriter.GetStringBuilder().ToString();
xmltext = xmltext.Replace("<?xml version="1.0" encoding="utf-16"?>", "<!DOCTYPE DMODULE " + publicIdentifier + ">");
xmltext = xmltext.Replace("<?xml version="1.0" encoding="utf-8"?>", "<!DOCTYPE DMODULE " + publicIdentifier + ">");
if (entitiesList.Count != 0)
{
string entityListAsOne = "";
foreach (string entity in entitiesList)
{
entityListAsOne = entityListAsOne + "rn" + entity;
}
xmltext = xmltext.Replace("//EN">", "//EN" [" + entityListAsOne + "]>");
}
File.WriteAllText(Path.Combine(outputFolder, fileNameNoExt + ".sgm"), xmltext);
}

}

一种方法是子类化适当的XmlWriter并覆盖WriteEndElement()以调用WriteFullEndElement()

例如,下面是完成这项工作的XmlTextWriter的子类化版本:

public class FullElementXmlTextWriter : XmlTextWriter
{
public FullElementXmlTextWriter(TextWriter w) : base(w) { }
public FullElementXmlTextWriter(Stream w, Encoding encoding) : base(w, encoding) { }
public FullElementXmlTextWriter(string filename, Encoding encoding) : base(filename, encoding) { }
public override void WriteEndElement()
{
base.WriteFullEndElement();
}
}

然后像这样使用它:

string xmltext;
using (var stringWriter = new StringWriter())
{
using (var xmlTextWriter = new FullElementXmlTextWriter(stringWriter))
{
myDoc.WriteTo(xmlTextWriter);
}
xmltext = stringWriter.ToString();
}

或者,如果您需要XmlWriterSettings提供的控件,则可以使用装饰器模式将任何XmlWriter封装在装饰器中,该装饰器会自动将调用从WriteEndElement()重新映射到WriteFullEndElement()

public class FullElementXmlWriterDecorator : XmlWriterDecorator
{
public FullElementXmlWriterDecorator(XmlWriter baseWriter) : base(baseWriter) { }
public override void WriteEndElement()
{
base.WriteFullEndElement();
}
}
public class XmlWriterDecorator : XmlWriter
{
readonly XmlWriter baseWriter;
public XmlWriterDecorator(XmlWriter baseWriter)
{
if (baseWriter == null)
throw new ArgumentNullException();
this.baseWriter = baseWriter;
}
protected virtual bool IsSuspended { get { return false; } }
public override void Close()
{
baseWriter.Close();
}
public override void Flush()
{
baseWriter.Flush();
}
public override string LookupPrefix(string ns)
{
return baseWriter.LookupPrefix(ns);
}
public override void WriteBase64(byte[] buffer, int index, int count)
{
if (IsSuspended)
return;
baseWriter.WriteBase64(buffer, index, count);
}
public override void WriteCData(string text)
{
if (IsSuspended)
return;
baseWriter.WriteCData(text);
}
public override void WriteCharEntity(char ch)
{
if (IsSuspended)
return;
baseWriter.WriteCharEntity(ch);
}
public override void WriteChars(char[] buffer, int index, int count)
{
if (IsSuspended)
return;
baseWriter.WriteChars(buffer, index, count);
}
public override void WriteComment(string text)
{
if (IsSuspended)
return;
baseWriter.WriteComment(text);
}
public override void WriteDocType(string name, string pubid, string sysid, string subset)
{
if (IsSuspended)
return;
baseWriter.WriteDocType(name, pubid, sysid, subset);
}
public override void WriteEndAttribute()
{
if (IsSuspended)
return;
baseWriter.WriteEndAttribute();
}
public override void WriteEndDocument()
{
if (IsSuspended)
return;
baseWriter.WriteEndDocument();
}
public override void WriteEndElement()
{
if (IsSuspended)
return;
baseWriter.WriteEndElement();
}
public override void WriteEntityRef(string name)
{
if (IsSuspended)
return;
baseWriter.WriteEntityRef(name);
}
public override void WriteFullEndElement()
{
if (IsSuspended)
return;
baseWriter.WriteFullEndElement();
}
public override void WriteProcessingInstruction(string name, string text)
{
if (IsSuspended)
return;
baseWriter.WriteProcessingInstruction(name, text);
}
public override void WriteRaw(string data)
{
if (IsSuspended)
return;
baseWriter.WriteRaw(data);
}
public override void WriteRaw(char[] buffer, int index, int count)
{
if (IsSuspended)
return;
baseWriter.WriteRaw(buffer, index, count);
}
public override void WriteStartAttribute(string prefix, string localName, string ns)
{
if (IsSuspended)
return;
baseWriter.WriteStartAttribute(prefix, localName, ns);
}
public override void WriteStartDocument(bool standalone)
{
baseWriter.WriteStartDocument(standalone);
}
public override void WriteStartDocument()
{
baseWriter.WriteStartDocument();
}
public override void WriteStartElement(string prefix, string localName, string ns)
{
if (IsSuspended)
return;
baseWriter.WriteStartElement(prefix, localName, ns);
}
public override WriteState WriteState
{
get { return baseWriter.WriteState; }
}
public override void WriteString(string text)
{
if (IsSuspended)
return;
baseWriter.WriteString(text);
}
public override void WriteSurrogateCharEntity(char lowChar, char highChar)
{
if (IsSuspended)
return;
baseWriter.WriteSurrogateCharEntity(lowChar, highChar);
}
public override void WriteWhitespace(string ws)
{
if (IsSuspended)
return;
baseWriter.WriteWhitespace(ws);
}
}

如果您正在进行异步写入,我相信(但尚未测试)您也想装饰异步方法。

然后像这样使用它:

string xmltext;
using (var stringWriter = new StringWriter())
{
using (var innerXmlWriter = XmlWriter.Create(stringWriter, settings))
using (var xmlTextWriter = new FullElementXmlWriterDecorator(innerXmlWriter))
{
myDoc.WriteTo(xmlTextWriter);
}
xmltext = stringWriter.ToString();
}

小提琴。

最新更新