2016-07-19 5 views
1

у меня есть XML-файл, например:XMLStarlet: Запросы и конкатенации вложенных дочерних элементов с помощью одного запроса

<?xml version="1.0" encoding="utf-8"?> 
<project> 
<data> 
    <modelType type="InstantMessage"> 
     <model type="InstantMessage" id="1" > 
      <modelField name="From" type="Party"> 
       <model type="Party" id="123456"> 
        <field name="Identifier" type="String"> 
         <value type="String">foo</value> 
        </field> 
       </model> 
      </modelField> 
      <multiModelField name="To" type="Party" /> 
       <field name="Body" type="String"> 
        <value type="String">bar</value> 
       </field> 
       <field name="TimeStamp" type="TimeStamp"> 
        <value type="TimeStamp">2016-07-11 13:26:38+02:00</value> 
       </field> 
     </model> 
    </modelType> 
</data> 

Я хочу, чтобы произвести следующий результат с помощью одного запроса:

foo|bar 

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

[email protected]:/.../# xmlstarlet sel -T -t -m /project/data/modelType/model -v "concat(/modelField/model/field/value'|'/field[@Body]/value)" file.xml 

но я постоянно получал синтаксические ошибки от xmlstarlet. Я не понимаю, как использовать его с manual. Кто-нибудь знает, как использовать xmlstarlet в этом случае?

Спасибо, Питер

ответ

2

Ваш XML файл (как представлено) не хватает закрывающий тег для <project>; что приведет к ошибке синтаксического анализа, которая предотвратит возможность выполнения запроса xmlstarlet.

Сам запрос имеет несколько проблем: в

  1. Синтаксис функции Concat является concat(a,b,c); ваш вызов не содержит запятых.

  2. Внутри соответствия xpaths относятся к совпадающему узлу. Но первый элемент в CONCAT:

    /modelField/model/field/value 
    

    является абсолютным, так что он может соответствовать только из корня, что он не делает. Вам нужно относительное выражение:

    modelField/model/field/value 
    

    или

    ./modelField/model/field/value 
    

    И последняя XPath:

    /field[@Body]/value 
    

    не будет найден, потому что field не является корневым элементом, и без / он не будет соответствовать, потому что field не является непосредственным дочерним элементом совпадающего узла. Здесь вы можете либо изложить путь от согласованного узла, как указано выше, или использовать // для выбора любого ребенка:

    .//field[@Body]/value 
    
  3. Однако спецификатор [@Body] неверен. Как написано, селектор преуспевает, если у элемента есть атрибут с именем Body. Вы пытаетесь сопоставить элемент с атрибутом name, значение которого составляет Body, которое вы бы указали как [@name="Body"]. Кавычки являются обязательными, что означает, что вам нужно использовать одинарные кавычки вокруг выражения или обратную косую черту - избегать кавычек.

Собирает все вместе, как только вы исправить файл XML, вы можете использовать:

xmlstarlet sel -T \ 
    -t -m /project/data/modelType/model \ 
    -v 'concat(modelField/model/field/value,"|",.//field[@name="Body"]/value)' \ 
    file.xml 

concat вызова не является необходимым, так как вы можете использовать несколько -v вариантов, и -o для вывода фиксированного строка. Возможно, вы найдете следующее более читаемым:

xmlstarlet sel -T \ 
    -t -m '/project/data/modelType/model' \ 
    -v './/field[@name="Identifier"]/value' \ 
    -o '|' \ 
    -v './/field[@name="Body"]/value' \ 
    file.xml 
+0

Отличное объяснение !! – user3586330

+0

Это такой отличный ответ! Единственное, что есть, состоит из двух отсутствующих котировок после '-m' и' [@ name = "Идентификатор"]/value' здесь, с добавленными котировками 'xmlstarlet sel -T \ -t -m '/ проект/данные/modelType/model '\ -v' .//field[@name="Identifier"]/value '\ -o' | ' \ -v './/field[@name="Body"]/value' \ file.xml' – jarisky

+0

@jarisky: Спасибо за корректуру. Исправлена. – rici