XPath查询从XML中提取信息

时间:2016-03-01 09:27:50

标签: xml xpath

这可能是一个棘手的问题,虽然我在XPath方面不是专家。几天来一直绞尽脑汁。 我正在尝试从XML文件中提取某些元素以创建一个表,然后我需要通过extractValue从中提取单个值:

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

表中的每一行(一列)都应包含XML的一个片段,其中所需元素的出现次数不超过一次。它的父节点也应该被提取到表中。问题是,在XML源代码中,必需的元素可能也有符合我要求的兄弟。我需要兄弟姐妹在我的结果表中以一个单独的行结束。

XML与此类似(只是内容更多,ID不同):

<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

我想要'赋值'元素,加上他们的祖先。但就像我上面所说的那样,我的restults表每行不得包含多个'assignment'元素。到目前为止我尝试过的东西似乎是在他们中的两个是兄弟姐妹的情况下提取的。 除此之外,“赋值”可以是“元素”的子元素或“字段”的子元素,这可能会增加复杂性。

为了使其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::*

可能会做到这一点,但我显然是错的。有没有办法达到我想要的目的?

1 个答案:

答案 0 :(得分: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元素。这些分配元素可以作为直接父级fieldelement。它们分别为idname elementfield作为唯一标识符。然后我们只是递归复制并进行额外的检查,如果id和/或名称匹配。我正在使用modes来确保我没有无限递归。

修改

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>