Мне нужно разделить большой XML-файл на несколько выходных xmls, используя XmlTextReader

Мне нужно взять XML-файл и создать несколько выходных XML-файлов из тысяч повторяющихся узлов входного файла. Исходный файл AnimalBatch.xml выглядит так:

<?xml version="1.0" encoding="utf-8" ?>
<Animals>
<Animal id="1001">
<Quantity>One</Quantity>
<Adjective>Red</Adjective>
<Name>Rooster</Name>
</Animal>
<Animal id="1002">
<Quantity>Two</Quantity>
<Adjective>Stubborn</Adjective>
<Name>Donkeys</Name>
</Animal>
<Animal id="1003">
<Quantity>Three</Quantity>
<Color>Blind</Color>
<Name>Mice</Name>
</Animal>
</Animals>

Но на самом деле символов CR / LF в нем нет. Фактический поток текста выглядит так:

<?xml version="1.0" encoding="utf-8" ?><Animals><Animal id="1001"><Quantity>One</Quantity><Adjective>Red</Adjective><Name>Rooster</Name></Animal><Animal id="1002"><Quantity>Two</Quantity><Adjective>Stubborn</Adjective><Name>Donkeys</Name></Animal><Animal id="1003"><Quantity>Three</Quantity><Color>Blind</Color><Name>Mice</Name></Animal></Animals>

Программа должна разделить повторяющееся «Животное» и создать 3 файла с именами: Animal_1001.xml, Animal_1002.xml и Animal_1003.xml.

У меня был предыдущий вопрос по этому поводу, используя XmlDocument, на который уже был дан ответ.
См .: [Разделение XML-файла на несколько XML-файлов с помощью XmlDocument] [1]

Этот вопрос касается того, как использовать XmlReader для захвата элементов и создания из них элементов XmlDocument.


Animal_1001.xml:
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>One</Quantity>
<Adjective>Red</Adjective>
<Name>Rooster</Name>
</Animal>


Animal_1002.xml
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>Two</Quantity>
<Adjective>Stubborn</Adjective>
<Name>Donkeys</Name>
</Animal>


Animal_1003.xml>
<?xml version="1.0" encoding="utf-8"?>
<Animal>
<Quantity>Three</Quantity>
<Adjective>Blind</Adjective>
<Name>Mice</Name>
</Animal>

Вот код, который работает - но только когда во входном файле есть разрывы строк:

    static void SplitXMLReader() 
    {
        string strFileName;
        string strSeq;

        XmlReader doc = XmlReader.Create("C:\\AnimalBatch.xml");

        while (doc.Read())
        {
            if (doc.Name=="Animal")
            {
                strSeq = doc.GetAttribute("id");

                XmlDocument outdoc = new XmlDocument();
                XmlDeclaration xmlDeclaration = outdoc.CreateXmlDeclaration("1.0", "utf-8", null);
                XmlElement rootNode = outdoc.CreateElement(doc.Name);

                rootNode.InnerXml = doc.ReadInnerXml();
                outdoc.InsertBefore(xmlDeclaration, outdoc.DocumentElement);
                outdoc.AppendChild(rootNode);

                strFileName = "Animal_" + strSeq + ".xml";
                outdoc.Save("C:\\" + strFileName);
            }
        }
    }

Когда эта программа запускается на копии «AnimalBatch.xml», которая имеет возврат каретки после каждого элемента - она ​​работает и создает файлы Animal_xxxx.xml по желанию. Когда AnimalBatch.xml выглядит как поток неформатированного текста - он получает первое Animal - и может получить его идентификатор 1001 и записать выходной файл нормально. Он может читать последующие элементы Animal, но не получает атрибут «id» - и в конечном итоге записывает выходные файлы с именем «Animal_.xml», поскольку, очевидно, переменная strSeq, которую он пытается прочитать из атрибута, имеет значение null или пустое значение. К концу второй файл содержит только это:

<?xml version="1.0" encoding="utf-8"?>
<Animal />

Это наводит меня на мысль, что XmlReader, по крайней мере, до степени метода doc.Read (), оператора (doc.Name == "Animal") или более позднего варианта "strSeq = doc.GetAttribute (" id ");" - работает иначе, если после тега <Animal id="1002"> стоит CR / LF.

Я предполагаю, что мой настоящий вопрос - когда это делает doc.GetAttribute ("id"); Где курсор в документе? И почему он не может получить единицы после «1001» - что работает?

Джон сказал, что XML не заботится о форматировании - и я всегда так думал, - но это сбивает с толку. Кроме того, для моего приложения единственный способ получить XML - неформатированный, поскольку я выхожу из SQL через SSIS, и это текстовый поток, а не объект XML.


person Rick Bellows    schedule 27.08.2012    source источник
comment
К вашему сведению, не используйте new XmlTextReader. Вместо этого используйте XmlReader.Create.   -  person John Saunders    schedule 27.08.2012
comment
Джон - Мне придется аплодировать вам стоя. Решением стало ваше наблюдение за использованием XmlReader вместо XmlTextReader. Проблема с программой чтения текста, по-видимому, была связана с тем, что он не распознал последующие элементы Animal (он получил бы первый, но в тот момент, когда я попытался получить идентификатор атрибута, он нашел только первый - и хорошо - я у меня на руках был беспорядок. Я выложу код, который теперь работает.   -  person Rick Bellows    schedule 28.08.2012
comment
Джон - Я обнаружил, что мой входной файл не имеет формата CR / LF, как показано в моем примере. Означает ли это, что мне нужно использовать xmlTextReader? Я достиг определенного уровня успеха с его использованием (т.е. я мог получить внешний xml - просто не мог извлечь атрибут ID). Может, стоит уточнить это в отдельном вопросе.   -  person Rick Bellows    schedule 28.08.2012
comment
Форматирование не имеет значения для XML.   -  person John Saunders    schedule 28.08.2012


Ответы (3)


Во-первых, я не вижу, чтобы вы что-либо присваивали outdoc где-либо ... Я полагаю, вы хотели заполнить его данными текущего узла, а затем сохранить? Кроме того, я бы создал один объект XmlDocument, а затем очистил / заполнил его в цикле, создание нового объекта в цикле пару тысяч раз - не самая лучшая идея ...

Также обратите внимание, что XmlReader перемещает по одному элементу за раз. Итак, ваш код atm:

  1. Позвоните XmlRead() и ни в коем случае не попадете (прочитал первое ?xml объявление)
  2. Вызвать XmlRead() один раз, попасть в дело, перейти к атрибуту id и записать пустой файл.
  3. Вызовите XmlRead() 10 раз \, пропуская все до следующего Animal элемента.

Одно из решений для захвата данных из тега <Animal> похоже на Это пример на msdn.

Во-вторых, подумайте о более удобном способе, например ReadInnerXml, например, с помощью ReadToFollowing. Также обратите внимание на метод GetAttribute.

Моя процедура будет такой:

  1. string toFile = "";
  2. Читать файл до тега <Animal>.
  3. GetAttribute("id");
  4. toFile = ReadInnerXml();
  5. Записать toFile в файл;)
  6. doc.ReadToFollowing("Animal");

Возможно, с некоторыми незначительными корректировками, поскольку я не проверяю то, что пишу с помощью компилятора ...

person wasyl    schedule 27.08.2012

Вам нужно создать корневой узел на outdoc. Используйте этот код:

    static void SplitXMLTextReader()
    {

        string strFileName;
        string strSeq = "0";

        XmlTextReader doc = new XmlTextReader(("C:\\AnimalBatch.xml"));
        doc.WhitespaceHandling = WhitespaceHandling.None;

        while (doc.Read())
        {
            switch (doc.Name)
            {
                case "Animal":
                    XmlDocument outdoc = new XmlDocument();
                   XmlDeclaration xmlDeclaration = outdoc.CreateXmlDeclaration("1.0", "utf-8", null);
                       XmlElement rootNode = outdoc.CreateElement(doc.Name);
                    rootNode.InnerXml = doc.ReadInnerXml();
                    outdoc.InsertBefore(xmlDeclaration, outdoc.DocumentElement);
                    outdoc.AppendChild(rootNode);


                    doc.MoveToFirstAttribute();
                    if (string.Compare(doc.Name, "id", true) == 0)
                    {
                        strSeq = doc.Value;
                    }
                    strFileName = "Animal_" + strSeq + ".xml";
                    outdoc.Save("C:\\" + strFileName);
                    break;
            }
        }

    }
person ígor    schedule 27.08.2012
comment
Это близкое решение, но с ошибкой: я получаю два выходных файла xml с именами Animal_0002.xml и Animal_003.xml. Animal_0002.xml имеет полный выходной файл, но содержит содержимое первого животного (одного красного петуха), а Animal_0002.xml имеет только пустой тег ‹Animal›, но не полезную нагрузку. Я думаю, что часть программы, которая захватывает идентификатор (раздел, начинающийся с doc.MoveToFirstAttribute ()), может нуждаться в получении информации из outdoc - после того, как она была добавлена ​​в outdoc. Однако - ваш код очень близок. Я вижу, как вы создаете Outdoc XmlDocuments внутри цикла doc.Read (). - person Rick Bellows; 27.08.2012

person    schedule
comment
OMG - я обнаружил, что мой пакетный xml-файл является текстовым потоком и не имеет CRLF, которые я указал в моем образце файла AnimalBatch.xml. Вышеупомянутое «решение» работает, когда после узла ‹Animal› есть CRLF, но не может использовать XmlReader, если его нет. Я вернулся к работе с XmlTextReader. Пух. - person Rick Bellows; 28.08.2012
comment
Ответ о том, как это сделать, когда во входном XML-файле нет перевода строки, находится под другим вопросом: stackoverflow.com/questions/12188383/ - person Rick Bellows; 30.08.2012