2015-10-26 4 views
0

Я пытаюсь объединить несколько файлов XML в одном, используя XmlReader и XmlWriter, хотя мой окончательный файл содержит только данные из последнего файла.Объединить XML-файлы с помощью XmlReader и XmlWriter

Я использую XmlReader и XmlWriter, потому что файлы XML для слияния большие по размеру.

Что я делаю неправильно в коде ниже?

class Program 
    { 
     static void Main(string[] args) 
     { 
      string folder = @"C:\Temp\"; 
      string output = folder + "_all.xml"; 
      Encoding readEncoding = System.Text.Encoding.Default; 

      XmlWriterSettings writerSettings = new XmlWriterSettings(); 
      writerSettings.Encoding = Encoding.UTF8; 
      writerSettings.ConformanceLevel = ConformanceLevel.Fragment; 

      XmlWriter writer = XmlWriter.Create(new StreamWriter(output, false), writerSettings); 
      bool firstFile = true; 

      foreach (FileInfo file in new DirectoryInfo(folder).GetFiles("*.xml").Where(f => f.Name != "_all.xml")) 
      { 
       XmlReader reader = XmlReader.Create(new StreamReader(file.FullName, readEncoding)); 
       while(reader.Read()) 
       { 

        switch (reader.NodeType) 
        { 
         case XmlNodeType.Element: 
          if (firstFile && reader.Name == "CYPHS:CYPHS") 
          { 
           writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); 
           writer.WriteAttributes(reader, true); 
          } 
          else if (firstFile && reader.Name == "CYP000") 
           writer.WriteStartElement(reader.Name); 
          else if (firstFile && reader.Name.StartsWith("C000")) 
           writer.WriteNode(reader, false); 
          else if (!firstFile && reader.Name != "CYPHS:CYPHS" && reader.Name != "CYP000" && !reader.Name.StartsWith("C000")) 
           writer.WriteNode(reader, false); 
          break; 

         default: 
          break; 
        } 
       } 

       firstFile = false; 
       reader.Close(); 
      } 

      writer.WriteEndElement(); 
      writer.WriteEndElement(); 

      writer.Close(); 

      Console.WriteLine("Done!"); 
      Console.ReadLine(); 
     } 
    } 

Файл 1

<CYPHS:CYPHS xsi:schemaLocation="http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5 CYPHSCYPHS_XMLSchema-v1-5.xsd" 
xmlns:CYPHS="http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <CYP000> 
    <C000010>File 1</C000010> 
    <CYP001> 
     <C001901>File 1</C001901> 
     <CYP101> 
     <C101902>File 1</C101902> 
     <CYP102> 
      <C102902>File 1</C102902> 
     </CYP102> 
     </CYP101> 
     <CYP002> 
     <C002901>File 1</C002901> 
     </CYP002> 
    </CYP001> 
    </CYP000> 
</CYPHS:CYPHS> 

Файл 2

<CYPHS:CYPHS xsi:schemaLocation="http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5 CYPHSCYPHS_XMLSchema-v1-5.xsd" 
xmlns:CYPHS="http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <CYP000> 
    <C000010>File 2</C000010> 
    <CYP001> 
     <C001901>File 2</C001901> 
     <CYP101> 
     <C101902>File 2</C101902> 
     <CYP102> 
      <C102902>File 2</C102902> 
     </CYP102> 
     </CYP101> 
     <CYP002> 
     <C002901>File 2</C002901> 
     </CYP002> 
    </CYP001> 
    </CYP000> 
</CYPHS:CYPHS> 

должны быть объединены в файл, как так:

<CYPHS:CYPHS xsi:schemaLocation="http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5 CYPHSCYPHS_XMLSchema-v1-5.xsd" 
xmlns:CYPHS="http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
    <CYP000> 
    <C000010>File 1</C000010> 
    <CYP001> 
     <C001901>File 1</C001901> 
     <CYP101> 
     <C101902>File 1</C101902> 
     <CYP102> 
      <C102902>File 1</C102902> 
     </CYP102> 
     </CYP101> 
     <CYP002> 
     <C002901>File 1</C002901> 
     </CYP002> 
    </CYP001> 
    <CYP001> 
     <C001901>File 2</C001901> 
     <CYP101> 
     <C101902>File 2</C101902> 
     <CYP102> 
      <C102902>File 2</C102902> 
     </CYP102> 
     </CYP101> 
     <CYP002> 
     <C002901>File 2</C002901> 
     </CYP002> 
    </CYP001> 
    </CYP000> 
</CYPHS:CYPHS> 

ответ

0

Я не совсем уверен, где вы пошли не так, но мне кажется, самым простым, чтобы проверить свойства Depth, LocalName и NamespaceURI из XmlReader при объединении файлов XML. Я настоятельно рекомендую против hardcoding пространство имен префиксы, так как префикс можно заменить любым другим префиксом без изменения семантики XML-файла.

Одно замечание: XmlWriter.WriteNode(XmlReader, bool) продвигает читателя к началу следующего узла, поэтому, если вы впоследствии звоните Read()и нет никаких пробелов в файле вы будете пропускать следующий элемент. Имея это в виду, при работе непосредственно с XmlReader, лучше протестировать как с интервалом, так и без него.

Таким образом:

public class XmlConcatenate 
{ 
    public static void ConcatenateAllFiles() 
    { 
     string folder = @"C:\Temp\"; 
     string output = folder + "_all.xml"; 
     Encoding readEncoding = System.Text.Encoding.Default; // WHY NOT Encoding.UTF8 !? 

     var files = new DirectoryInfo(folder).GetFiles("*.xml").Where(f => f.Name != "_all.xml").Select(f => f.FullName).Select(n => (TextReader)new StreamReader(n, readEncoding)); 

     using (var textWriter = new StreamWriter(output, false)) 
     { 
      Concatenate(files, textWriter); 
     } 
    } 

    public static void Concatenate(IEnumerable<TextReader> inputs, TextWriter output) 
    { 
     var writerSettings = new XmlWriterSettings() { Encoding = Encoding.UTF8, ConformanceLevel = ConformanceLevel.Fragment }; 
     var whiteSpace = new StringBuilder(); 
     int indent = 0; 

     using (var writer = XmlWriter.Create(output, writerSettings)) 
     { 
      var writeDepth = 0; 
      var first = true; 

      foreach (var input in inputs) 
      { 
       using (input) 
       using (var reader = XmlReader.Create(input)) 
       { 
        bool alreadyRead = false; 
        while (!reader.EOF && (alreadyRead || reader.Read())) 
        { 
         alreadyRead = false; 
         switch (reader.NodeType) 
         { 
          case XmlNodeType.Element: 
           { 
            if (reader.Depth == 0 && reader.LocalName == "CYPHS" && reader.NamespaceURI == "http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5") 
            { 
             if (writeDepth == 0) 
             { 
              writer.WriteWhitespace(whiteSpace.ToString()); 
              writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); 
              writer.WriteAttributes(reader, true); 
              writeDepth++; 
             } 
            } 
            else if (reader.Depth == 1 && reader.LocalName == "CYP000" && reader.NamespaceURI == "") 
            { 
             if (writeDepth == 1) 
             { 
              indent = whiteSpace.ToString().Replace("\n", "").Replace("\r", "").Length; 

              writer.WriteWhitespace(whiteSpace.ToString()); 
              writer.WriteStartElement(reader.LocalName, reader.NamespaceURI); 
              writeDepth++; 
             } 
            } 
            else if (reader.Depth == 2) 
            { 
             if (reader.LocalName.StartsWith("C000") && reader.NamespaceURI == "") 
             { 
              if (first) 
              { 
               first = false; 
               writer.WriteWhitespace(whiteSpace.ToString()); 
               writer.WriteNode(reader, false); 
               alreadyRead = true; 
              } 
             } 
             else 
             { 
              writer.WriteWhitespace(whiteSpace.ToString()); 
              writer.WriteNode(reader, false); 
              alreadyRead = true; 
             } 
            } 
            whiteSpace.Length = 0; // Clear accumulated whitespace. 
           } 
           break; 
          case XmlNodeType.Whitespace: 
           { 
            whiteSpace.Append(reader.Value); 
           } 
           break; 
          default: 
           break; 
         } 
        } 
       } 
      } 
      while (writeDepth-- > 0) 
      { 
       if (indent > 0) 
        writer.WriteWhitespace("\n" + new string(' ', indent * writeDepth)); 
       writer.WriteEndElement(); 
      } 
     } 
    } 
} 

Bit неприятность получать расстояние до слияния, если вы не заботитесь о сохранении расстояния вы можете упростить код по существу.

fiddle.

Возможно, вы не захотите использовать System.Text.Encoding.Default для чтения ваших XML-файлов. Из документов:

Поскольку все кодировки по умолчанию теряют данные, вместо этого вы можете использовать UTF8. UTF-8 часто идентичен в диапазоне U + 00 до U + 7F, но может кодировать другие символы без потерь.

0

Like This

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Xml; 
using System.Xml.Linq; 


namespace ConsoleApplication53 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string file1 = 
       "<CYPHS:CYPHS xsi:schemaLocation=\"http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5 CYPHSCYPHS_XMLSchema-v1-5.xsd\"" + 
        " xmlns:CYPHS=\"http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5\"" + 
        " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" + 
         "<CYP000>" + 
         "<C000010>File 1</C000010>" + 
         "<CYP001>" + 
          "<C001901>File 1</C001901>" + 
          "<CYP101>" + 
          "<C101902>File 1</C101902>" + 
          "<CYP102>" + 
           "<C102902>File 1</C102902>" + 
          "</CYP102>" + 
          "</CYP101>" + 
          "<CYP002>" + 
          "<C002901>File 1</C002901>" + 
          "</CYP002>" + 
         "</CYP001>" + 
         "</CYP000>" + 
        "</CYPHS:CYPHS>"; 
      XDocument doc1 = XDocument.Parse(file1); 

      XElement doc1_CYP000 = doc1.Descendants("CYP000").FirstOrDefault(); 

      string file2 = 
       "<CYPHS:CYPHS xsi:schemaLocation=\"http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5 CYPHSCYPHS_XMLSchema-v1-5.xsd\"" + 
       " xmlns:CYPHS=\"http://www.datadictionary.nhs.uk/messages/CYPHS-v1-5\"" + 
       " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">" + 
        "<CYP000>" + 
        "<C000010>File 2</C000010>" + 
        "<CYP001>" + 
         "<C001901>File 2</C001901>" + 
         "<CYP101>" + 
         "<C101902>File 2</C101902>" + 
         "<CYP102>" + 
          "<C102902>File 2</C102902>" + 
         "</CYP102>" + 
         "</CYP101>" + 
         "<CYP002>" + 
         "<C002901>File 2</C002901>" + 
         "</CYP002>" + 
        "</CYP001>" + 
        "</CYP000>" + 
       "</CYPHS:CYPHS>"; 

      XDocument doc2 = XDocument.Parse(file2); 

      XElement doc2_CYP000 = doc2.Descendants("CYP000").FirstOrDefault(); 
      doc1_CYP000.Add(doc2_CYP000.Descendants()); 

     } 

    } 
} 
Смежные вопросы