2016-03-01 2 views
2

Это может быть сложно, хотя я не эксперт, когда дело доходит до XPath. Много дней ломал голову. Я пытаюсь извлечь определенные элементы из файла XML, чтобы создать таблицу, из которой я тогда нужно извлечь отдельные значения с помощью EXTRACTVALUE:Запрос XPath для извлечения информации из XML

SELECT [extractValue statements] FROM TABLE(XMLSequence(extract(lv_xml, lv_xpath))) t; 

Каждая строка в таблице (один столбец) должен содержать один фрагмент XML, где требуемый элемент встречается не более одного раза. Его родительские узлы также должны быть извлечены в таблицу. Дело в том, что в источнике XML требуемый элемент может иметь родных братьев, которые также соответствуют моим требованиям. Мне нужно, чтобы братья и сестры оказались в отдельной строке в моей таблице результатов.

XML-похож на это (только с намного большим количеством контента, а также с различными идентификаторами):

<element id="Address"> 
    <assignment name="name1" category="cat1" /> 
    <field id="field1"> 
    </field> 
    <field id="field2"> 
    </field> 
    <field id="field3"> 
    <assignment name="name5" category="cat2" /> 
    <assignment name="name12" category="cat2" /> 
    </field> 
</element> 
<element id="PersonInfo"> 
    <field id="field1"> 
    </field> 
    <field id="field2"> 
    <assignment name="name17" category="cat1" /> 
    </field> 
    <field id="field3"> 
    </field> 
    <field id="field4"> 
    </field> 
</element> 
etc etc 

Я хочу элементов «задание», плюс их предков. Но, как я уже сказал выше, моя таблица «Осталось» не должна содержать более одного элемента «присваивания» в строке. То, что я пробовал до сих пор, кажется, извлекает лот, если двое из них являются братьями и сестрами. В дополнение к этому «присваивание» может быть дочерним элементом «элемента» или дочернего элемента «поля», что, вероятно, добавляет сложности.

Чтобы сделать 100% ясно, мой результат должен выглядеть следующим образом (в порядке для последующего EXTRACTVALUE работать):

<element id="Address"> 
    <assignment name="name1" category="cat1" /> 
</element> 

<element id="Address"> 
    <field id="field3"> 
    <assignment name="name5" category="cat2" /> 
    </field> 
</element> 

<element id="Address"> 
    <field id="field3"> 
    <assignment name="name12" category="cat2" /> 
    </field> 
</element> 

<element id="PersonInfo"> 
    <field id="field2"> 
    <assignment name="name17" category="cat1" /> 
    </field> 
</element> 

Я играл вокруг с XPath запросов, но не повезло так далеко. Я ожидал, что

//assignment/ancestor-or-self::* 

может сделать трюк, но я, очевидно, был неправ. Есть ли способ достичь того, чего я хочу?

+0

Таким образом, каждая ваша запись имеет верхнюю часть самого родителя: 'element'? – Neijwiert

+0

Спасибо за ваш комментарий - Да. всегда существует. существует иногда, но не всегда. – Amalia

+0

Можете ли вы использовать XSLT? – Neijwiert

ответ

1

Учитывая вход:

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

<root> 
    <element id="Address"> 
    <assignment name="name1" category="cat1"/> 
    <field id="field1"></field> 
    <field id="field2"></field> 
    <field id="field3"> 
     <assignment name="name5" category="cat2"/> 
     <assignment name="name12" category="cat2"/> 
    </field> 
    </element> 
    <element id="PersonInfo"> 
    <field id="field1"></field> 
    <field id="field2"> 
     <assignment name="name17" category="cat1"/> 
    </field> 
    <field id="field3"></field> 
    <field id="field4"></field> 
    </element> 
</root> 

С данной XSL таблицы стилей:

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:strip-space elements="*"/> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/"> 
    <xsl:apply-templates select="//assignment"/> 
</xsl:template> 

<xsl:template match="assignment"> 
    <xsl:apply-templates select="ancestor::element"> 
    <xsl:with-param name="assignment-name" select="@name"/> 
    <xsl:with-param name="field-id" select="ancestor::field/@id"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="element"> 
    <xsl:param name="assignment-name"/> 
    <xsl:param name="field-id"/> 

    <xsl:copy> 
    <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
     <xsl:with-param name="field-id" select="$field-id"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="@*|node()" mode="copy"> 
    <xsl:param name="assignment-name"/> 
    <xsl:param name="field-id"/> 

    <xsl:copy> 
    <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
     <xsl:with-param name="field-id" select="$field-id"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="field" mode="copy"> 
    <xsl:param name="assignment-name"/> 
    <xsl:param name="field-id"/> 

    <xsl:if test="@id=$field-id"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
     <xsl:with-param name="field-id" select="$field-id"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:if> 
</xsl:template> 

<xsl:template match="assignment" mode="copy"> 
    <xsl:param name="assignment-name"/> 
    <xsl:param name="field-id"/> 

    <xsl:if test="@name=$assignment-name"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
     <xsl:with-param name="field-id" select="$field-id"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:if> 
</xsl:template> 

</xsl:stylesheet> 

даст выход:

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

<element id="Address"> 
    <assignment name="name1" category="cat1"/> 
</element> 
<element id="Address"> 
    <field id="field3"> 
     <assignment name="name5" category="cat2"/> 
    </field> 
</element> 
<element id="Address"> 
    <field id="field3"> 
     <assignment name="name12" category="cat2"/> 
    </field> 
</element> 
<element id="PersonInfo"> 
    <field id="field2"> 
     <assignment name="name17" category="cat1"/> 
    </field> 
</element> 

Объяснение:

Во-первых, мы должны соответствовать против всех assignment элементов. Эти элементы присваивания могут либо иметь field, либо element в качестве прямого родителя. Они имеют уникальный идентификатор своих id и name для element и field соответственно. Затем мы просто рекурсивная копия и делаем дополнительную проверку, если id и/или имя совпадают. Я использую modes, чтобы убедиться, что у меня нет бесконечной рекурсии.

EDIT

проверка Id не представляется необходимым, вы можете просто проверить, если он имеет назначение ребенка с именем. Это решение работает лучше, потому что оно позволяет встраивать field элементов.

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:strip-space elements="*"/> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:template match="/"> 
    <xsl:apply-templates select="//assignment"/> 
</xsl:template> 

<xsl:template match="assignment"> 
    <xsl:apply-templates select="ancestor::element"> 
    <xsl:with-param name="assignment-name" select="@name"/> 
    </xsl:apply-templates> 
</xsl:template> 

<xsl:template match="element"> 
    <xsl:param name="assignment-name"/> 

    <xsl:copy> 
    <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="@*|node()" mode="copy"> 
    <xsl:param name="assignment-name"/> 

    <xsl:copy> 
    <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
    </xsl:apply-templates> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="field" mode="copy"> 
    <xsl:param name="assignment-name"/> 

    <xsl:if test="child::assignment[@name=$assignment-name]"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:if> 
</xsl:template> 

<xsl:template match="assignment" mode="copy"> 
    <xsl:param name="assignment-name"/> 

    <xsl:if test="@name=$assignment-name"> 
    <xsl:copy> 
     <xsl:apply-templates select="@*|node()" mode="copy"> 
     <xsl:with-param name="assignment-name" select="$assignment-name"/> 
     </xsl:apply-templates> 
    </xsl:copy> 
    </xsl:if> 
</xsl:template> 

</xsl:stylesheet> 
+0

Это великолепно. Огромное спасибо. Я играл с ним и настраивал его в соответствии с моим фактическим сценарием, и он работает блестяще - за исключением одного случая. Значение атрибута «имя» для элементов «присваивания» не обязательно должно быть уникальным. Элемент «элемент» или «поле» может иметь несколько элементов «присваивания», поскольку дети с одинаковым именем («категория» также может быть идентичной, но это очень маловероятно). В этих случаях вывод XSL-преобразования будет содержать несколько тегов «присваивания» для каждого раздела, что означает, что я не могу обработать его. Есть ли способ справиться с этим? – Amalia

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