2017-03-22 21 views
6

Zanim zostaną przeskoczeni przez ludzi mówiąc, że parser XML nie powinien przejmować się, czy elementy są puste czy zamknięte, jest powód, dla którego nie mogę pozwolić na samo-zamknięte elementy XML. Powodem jest to, że faktycznie pracuję z SGML, a nie XML, a DTD SGML, z którym pracuję, jest bardzo surowy i nie pozwala na to.Jak zatrzymać samoczynne zamykanie pustych elementów XML za pomocą XmlDocument w języku C#?

Mam kilka tysięcy plików SGML, które muszę uruchomić na XSLT. Musiałem więc tymczasowo przekonwertować SGML na XML, aby zastosować XSLT. Napisałem następnie metodę, która zamienia je z powrotem na SGML (zasadniczo po prostu zastępując deklarację XML deklaracją SGML i odpisując wszelkie inne deklaracje jednostki, takie jak elementy graficzne).

Mój problem polega na tym, że po tej konwersji z powrotem do SGML, kiedy otwieram pliki w moim edytorze SGML, pliki nie parsują się, ponieważ puste elementy zostały samozamykane.

Czy ktoś wie, jak mogę temu zapobiec, jeśli używasz XmlDocument?

Metody, które przekształcają się SGML do XML iz powrotem są przedstawione poniżej

//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 + "\r\n" + entity; 
        } 

        xmltext = xmltext.Replace("//EN\">", "//EN\" [" + entityListAsOne + "]>"); 
       } 

       File.WriteAllText(Path.Combine(outputFolder, fileNameNoExt + ".sgm"), xmltext); 
      } 


     } 

Odpowiedz

2

Jednym ze sposobów byłoby podklasy odpowiedniej XmlWriter i zastąpić WriteEndElement() zadzwonić WriteFullEndElement().

Na przykład, oto podklasy wersja XmlTextWriter że spełnia swoje zadanie:

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(); 
    } 
} 

następnie użyć go lubię:

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

Lub, jeśli potrzebujemy kontroli zapewnianej przez XmlWriterSettings, można użyj wzoru dekoratora do enkapsulacji dowolnego XmlWriter w dekoratorze, który automatycznie zmienia przypisania połączeń z WriteEndElement() na 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); 
    } 
} 

Jeśli robisz pismo asynchroniczne, uważam (ale nie przetestowałem), że chcesz również udekorować metody asynchroniczne.

następnie użyć go lubię:

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(); 
} 

fiddle.

+0

Doskonała i kompleksowa odpowiedź. Dziękuję Ci bardzo. Idealny. –