Извлеките несколько полей с одинаковыми именами полей, используя xmlint

У меня есть файл XML с большим количеством медиа-полей. Пример XML:

<root>
    <item>
        <name>Item 1</name>
        <mediaList>
            <media>
                <name>Name 1</name>
                <URL><![CDATA[http://example.com/image1.jpg]]></URL>
            </media>
            <media>
                <name>Name 2</name>
                <URL><![CDATA[http://example.com/image2.jpg]]></URL>
            </media>
        </mediaList>
    </item>
    <item>
        <name>Item 2</name>
        <mediaList>
            <media>
                <name>Name 3</name>
                <URL><![CDATA[http://example.com/image3.jpg]]></URL>
            </media>
            <media>
                <name>Name 4</name>
                <URL><![CDATA[http://example.com/image4.jpg]]></URL>
            </media>
        </mediaList>
    </item>
</root>

Все элементы строятся одинаково. Используя XMLLint с XPath, я пытаюсь получить список всех URL-адресов. Однако до сих пор я не нашел лучшего способа сделать это. Вот некоторые из способов, которые я пробовал:

xmllint --xpath "string(/root/item/mediaList/URL)" file.xml >> log.txt

Этот возвращает хороший URL, но останавливается после первого элемента (давая мне только 1 изображение)

xmllint --xpath "/root/item/mediaList/URL" file.xml >> log.txt

Это дает мне все элементы, но все находится в одной строке и отображается как <URL><![CDATA[http://example.com/image.jpg]]></URL> для каждого элемента.

xmllint --xpath "/root/item/mediaList/URL/text()" file.xml >> log.txt

Это ближе всего, но по-прежнему возвращает теги <![CDATA[]]> вокруг него, и снова все в одной строке.

Я также пробовал перебирать элементы, но это было очень медленно и не работало должным образом.

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

http://example.com/image1.jpg
http://example.com/image2.jpg
http://example.com/image3.jpg
http://example.com/image4.jpg

person Sander Koedood    schedule 14.02.2017    source источник
comment
Опция --nocdata извлекает текст из каждого узла CDATA. Однако я не уверен, как получить каждый URL-адрес в отдельной строке.   -  person chepner    schedule 14.02.2017
comment
Спасибо! Хороший. Одной проблемой меньше.   -  person Sander Koedood    schedule 14.02.2017


Ответы (2)


xmllint не поддерживает string(...) для нескольких совпадений XPath. (Поэтому он показывает только 1-й результат).

Вы можете использовать xmlstarlet как:

xmlstarlet sel -T -t -m /root/item/mediaList/media/URL -v . -n file.xml

и это производит

http://example.com/image1.jpg
http://example.com/image2.jpg
http://example.com/image3.jpg
http://example.com/image4.jpg

или также perl (с установленным модулем XML::LibXML) как:

perl -MXML::LibXML -E 'say $_->to_literal for XML::LibXML->load_xml(location=>q{file.xml})->findnodes(q{/root/item/mediaList/media/URL})'

также дает тот же результат, что и выше.

person jm666    schedule 14.02.2017
comment
Вариант xmlstarlet решил это для меня. Спасибо. - person Sander Koedood; 14.02.2017

Я думаю, вам следует изменить синтаксический анализатор в документе W3C:

Каждый символ в разделе CDATA обрабатывается как символьные данные. Таким образом, ‹![CDATA[‹]]> в исходном документе будет обрабатываться так же, как ‹. Оба приведут к одному символу ‹ в текстовом узле дерева. Таким образом, раздел CDATA обрабатывается так, как если бы ‹![CDATA[ и ]]> были удалены, а каждое вхождение ‹ и & заменено на ‹ и & соответственно.

CDATA будет удален автоматически, я тестирую его на питоне:

tree = etree.fromstring(xml)
tree.xpath('//URL/text()')

из:

['http://example.com/image1.jpg',
 'http://example.com/image2.jpg',
 'http://example.com/image3.jpg',
 'http://example.com/image4.jpg']

Ваш XPath правильный.

person 宏杰李    schedule 14.02.2017