Как эффективно разобрать составные XML-документы из файла - PullRequest
9 голосов
/ 24 августа 2009

У меня есть файл, который состоит из сцепленных действительных документов XML. Я хотел бы эффективно разделять отдельные XML-документы.

Содержимое объединенного файла будет выглядеть следующим образом, поэтому составной файл сам по себе не является допустимым XML-документом.

<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>

Каждый отдельный XML-документ занимает около 1-4 КБ, но потенциально их может быть несколько сотен. Все XML-документы соответствуют одной и той же XML-схеме.

Есть предложения или инструменты? Я работаю в среде Java.

Редактировать: я не уверен, будет ли xml-объявление присутствовать в документах или нет.

Редактировать: Предположим, что кодировка для всех документов xml - UTF-8.

Ответы [ 5 ]

4 голосов
/ 24 августа 2009

Не делись! Добавьте один большой тег вокруг него! Затем он снова становится одним XML-файлом:

<BIGTAG>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
<?xml version="1.0" encoding="UTF-8"?>
<someData>...</someData>
</BIGTAG>

Теперь использование / BIGTAG / SomeData даст вам все корни XML. <ч /> Если обработка инструкций мешает, вы всегда можете использовать RegEx для их удаления. Проще просто удалить все инструкции по обработке, чем использовать RegEx для поиска всех корневых узлов. Если кодировка отличается для всех документов, помните следующее: весь документ должен быть закодирован каким-либо типом кодировки, поэтому все включенные в него XML-документы будут использовать одну и ту же кодировку, независимо от того, что говорит каждый заголовок. Если большой файл закодирован как UTF-16, то не имеет значения, если в инструкциях по обработке XML указано, что сам XML является UTF-8. Это не будет UTF-8, так как весь файл - UTF-16. Следовательно, кодировка в этих инструкциях по обработке XML недопустима.

Объединяя их в один файл, вы изменили кодировку ... <ч /> Под RegEx я имею в виду регулярные выражения. Вы просто должны удалить весь текст, который находится между <? и?> который не должен быть слишком сложным с регулярным выражением и немного более сложным, если вы пытаетесь использовать другие методы манипуляции со строками.

3 голосов
/ 24 августа 2009

Как говорит Имон, если вы знаете, что <? Xml> всегда будет рядом, просто прекратите.

В противном случае ищите конечный тег уровня документа. То есть, отсканируйте текст, считая, сколько уровней вы находитесь. Каждый раз, когда вы видите тег, который начинается с «<», но не «</» и не заканчивается на «/>», добавляйте 1 к счетчику глубины. Каждый раз, когда вы видите тэг, начинающийся с «</», вычитайте 1. Каждый раз, когда вы вычитаете 1, проверяйте, находитесь ли вы на нуле. Если это так, вы достигли конца XML-документа. </p>

3 голосов
/ 24 августа 2009

Поскольку вы не уверены, что объявление всегда будет присутствовать, вы можете удалить все объявления (например, может найти их регулярное выражение <\?xml version.*\?>), добавив <doc-collection>, добавив </doc-collection>, так что результирующая строка будет быть действительным XML-документом. В нем вы можете получить отдельные документы, используя (например) запрос XPath /doc-collection/*. Если объединенный файл может быть достаточно большим, чтобы потребление памяти стало проблемой, вам может понадобиться потоковый анализатор, такой как Sax, но принцип остается тем же.

В аналогичном сценарии, с которым я столкнулся, я просто читаю составленный документ напрямую, используя xml-анализатор: хотя составной файл может не являться действительным xml документом , это действительный xml фрагмент (без повторных объявлений) - поэтому, если вы удалите объявления, если ваш анализатор поддерживает синтаксический анализ фрагментов, вы также можете просто прочитать результат напрямую. Все элементы верхнего уровня будут корневыми элементами объединенных документов.

Короче говоря, если вы удалите все объявления, у вас будет действительный xml-фрагмент, который легко разбирается напрямую или с помощью тега.

1 голос
/ 17 ноября 2013

Это мой ответ для версии C #. очень уродливый код, который работает: - \

public List<T> ParseMultipleDocumentsByType<T>(string documents)
    {
        var cleanParsedDocuments = new List<T>();
        var serializer = new XmlSerializer(typeof(T));
        var flag = true;
        while (flag)
        {
            if(documents.Contains(typeof(T).Name))
            {
                var startingPoint = documents.IndexOf("<?xml");
                var endingString = "</" +typeof(T).Name + ">";
                var endingPoing = documents.IndexOf(endingString) + endingString.Length;
                var document = documents.Substring(startingPoint, endingPoing - startingPoint);
                var singleDoc = (T)XmlDeserializeFromString(document, typeof(T));
                cleanParsedDocuments.Add(singleDoc);
                documents = documents.Remove(startingPoint, endingPoing - startingPoint);
            }
            else
            {
                flag = false;
            }
        }


        return cleanParsedDocuments;
    }

    public static object XmlDeserializeFromString(string objectData, Type type)
    {
        var serializer = new XmlSerializer(type);
        object result;

        using (TextReader reader = new StringReader(objectData))
        {
            result = serializer.Deserialize(reader);
        }

        return result;
    }
0 голосов
/ 24 марта 2010

У меня нет ответа на Java, но вот как я решил эту проблему с C #.

Я создал класс с именем XmlFileStreams, чтобы сканировать исходный документ для объявления документа XML и логически разбивать его на несколько документов:

class XmlFileStreams {

    List<int> positions = new List<int>();
    byte[] bytes;

    public XmlFileStreams(string filename) {
        bytes = File.ReadAllBytes(filename);
        for (int pos = 0; pos < bytes.Length - 5; ++pos)
            if (bytes[pos] == '<' && bytes[pos + 1] == '?' && bytes[pos + 2] == 'x' && bytes[pos + 3] == 'm' && bytes[pos + 4] == 'l')
                positions.Add(pos);
        positions.Add(bytes.Length);
    }

    public IEnumerable<Stream> Streams {
        get {
            if (positions.Count > 1)
                for (int i = 0; i < positions.Count - 1; ++i)
                    yield return new MemoryStream(bytes, positions[i], positions[i + 1] - positions[i]);
        }
    }

}

Чтобы использовать XmlFileStreams:

foreach (Stream stream in new XmlFileStreams(@"c:\tmp\test.xml").Streams) {
    using (var xr = XmlReader.Create(stream, new XmlReaderSettings() { XmlResolver = null, ProhibitDtd = false })) {
        // parse file using xr
    }
}

Есть несколько предостережений.

  1. Читает весь файл в память для обработки. Это может быть проблемой, если файл действительно большой.
  2. Он использует простой перебор для поиска границ документа XML.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...