Как остановить самозакрывание пустых XML-элементов с помощью XmlDocument в C#?

прежде чем на меня набросятся люди, говорящие, что синтаксический анализатор 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);
            }


        }

1 ответов


один из способов сделать это - подкласс соответствующего 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() to 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();
}

скрипка.