【问题标题】:XSLT fails to transform bigger XML fileXSLT 无法转换更大的 XML 文件
【发布时间】:2014-09-28 19:30:43
【问题描述】:

我是 XML 和 XSLT 的新手,我想从 XML 文件中过滤一些信息。基于 XML 文件中某些标记值的匹配。当 XML 文件仅包含 1 或 2 个人标签信息时,我的解决方案有效。但是当使用具有更多个人信息的更大 xml 文件时。它失败了,只有最后一个人根据需要进行转换。

这是我的 XML 文件,如下所示:

<People>
<Person>
    <required-tag1>some-information</required-tag1>
    <required-tag2>some-information</required-tag2>
    <tag3>not important info</tag3>
    <tag4>not important info</tag4>
    <first-name>Mike</first-name>
    <last-name>Hewitt</last-name>
    <licenses>
        <license>
            <number>938387</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TX</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
        <license>
            <number>938387</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">IL</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
    </licenses>
    <appointments>
        <appointment-info>
            <code>5124</code>
            <number>14920329324</number>
            <licensed-states>
                <state>TX</state>
            </licensed-states>
        </appointment-info>
    </appointments>
</Person>
<Person>
    <required-tag1>some-information</required-tag1>
    <required-tag2>some-information</required-tag2>
    <tag3>not important info</tag3>
    <tag4>not important info</tag4>
    <first-name>John</first-name>
    <last-name>Jhonny</last-name>
    <licenses>
        <license>
            <number>1762539</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TX</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
        <license>
            <number>1762539</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">NY</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
    </licenses>
    <appointments>
        <appointment-info>
            <code>5124</code>
            <number>14920329324</number>
            <licensed-states>
                <state>TX</state>
            </licensed-states>
        </appointment-info>
    </appointments>
</Person>
    <Person>
    <required-tag1>some-information</required-tag1>
    <required-tag2>some-information</required-tag2>
    <tag3>not important info</tag3>
    <tag4>not important info</tag4>
    <first-name>Danny</first-name>
    <last-name>Hewitt</last-name>
    <licenses>
        <license>
            <number>17294083</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">IL</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
    </licenses>
    <appointments>
        <appointment-info>
            <code>5124</code>
            <number>14920329324</number>
            <licensed-states>
                <state>IL</state>
            </licensed-states>
        </appointment-info>
    </appointments>
</Person>
<Person>
    <required-tag1>some-information</required-tag1>
    <required-tag2>some-information</required-tag2>
    <tag3>not important info</tag3>
    <tag4>not important info</tag4>
    <first-name>Russel</first-name>
    <last-name>Jhonny</last-name>
    <licenses>
        <license>
            <number>840790</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">TX</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
        <license>
            <number>840790</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">NY</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
        <license>
            <number>840790</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">CA</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
    </licenses>
    <appointments>
        <appointment-info>
            <code>5124</code>
            <number>14920329324</number>
            <licensed-states>
                <state>TX</state>
                <state>NY</state>
            </licensed-states>
        </appointment-info>
    </appointments>
</Person>
</People>

我想做的基本上是,如果一个人在某个州获得许可,例如德克萨斯州。并且在那个州有约会信息,例如 TX,从许可证中过滤掉。如果这是唯一的许可证信息,则过滤此人。

并且新的 xml 应该包含所需标签的信息。并且只有与预约许可证中的许可证不匹配的许可证才会说明并过滤匹配所有许可证的人。

这是我期望的输出:

<People>
<Person>
    <required-tag1>some-information</required-tag1>
    <required-tag2>some-information</required-tag2>
    <first-name>Mike</first-name>
    <last-name>Hewitt</last-name>
    <licenses>
        <license>
            <number>938387</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">IL</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
    </licenses>
</Person>
<Person>
    <required-tag1>some-information</required-tag1>
    <required-tag2>some-information</required-tag2>
    <first-name>John</first-name>
    <last-name>Jhonny</last-name>
    <licenses>
        <license>
            <number>1762539</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">NY</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
    </licenses>
</Person>
<Person>
    <required-tag1>some-information</required-tag1>
    <required-tag2>some-information</required-tag2>
    <first-name>John</first-name>
    <last-name>Jhonny</last-name>
    <licenses>
        <license>
            <number>840790</number>
            <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">CA</state>
            <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
        </license>
    </licenses>
</Person>
</People>

与该州的所有许可证匹配的第三人被过滤掉。目前我在示例中只使用了一种状态,但如果有多种状态,它应该能够过滤该信息。

如何编写 XSLT 来过滤此信息。我正在使用 XSLT 1.0 版

目前我能够应用此 XSLT 来获取转换所需的标签。但我不知道如何过滤许可证状态,它适用于较小的文件,但当我处理更大的文件时会失败。如果有人能指导我,我将不胜感激,因为我不明白出了什么问题,哪里出了问题。

这是我使用的 XSLT,如下所示:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>

<!--Identity transform (aka identity template). This will match
and copy attributes and nodes (element, text, comment and
processing-instruction) without changing them. Unless a more
specific template matches, everything will get handled by this
template.-->    
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<!--This template will match the "Person" element node. The "xsl:copy"
creates the new "Person" element. The "xsl:apply-templates" tells
the processor to apply templates to any attributes (of Person) or
elements listed in the "select". (Other elements will not be 
processed.) I used the union operator in the "select" so I wouldn't
have to write multiple "xsl:apply-templates".-->
<xsl:template match="Person">
    <xsl:copy>
        <xsl:apply-templates select="@*|first-name|last-name|
            required-tag1|required-tag2|licenses"/>
    </xsl:copy>
</xsl:template>

<!--This template will match any "license" element nodes that have a child 
"state" element whose value matches a "state" element node that is a 
child of "licensed-states". Since the "xsl:template" is empty, nothing 
is output or processed further.-->
<xsl:template match="license[state=//licensed-states/state]"/>

</xsl:stylesheet>

这就是我得到的输出,这是错误的。

<?xml version="1.0" encoding="UTF-8"?>
<People>
<Person>
  <required-tag1>some-information</required-tag1>
  <required-tag2>some-information</required-tag2>
  <first-name>Mike</first-name>
  <last-name>Hewitt</last-name>
  <licenses/>
</Person>
<Person>
   <required-tag1>some-information</required-tag1>
  <required-tag2>some-information</required-tag2>
  <first-name>John</first-name>
  <last-name>Jhonny</last-name>
  <licenses/>
</Person>
<Person>
  <required-tag1>some-information</required-tag1>
  <required-tag2>some-information</required-tag2>
  <first-name>Danny</first-name>
  <last-name>Hewitt</last-name>
  <licenses/>
</Person>
<Person>
  <required-tag1>some-information</required-tag1>
  <required-tag2>some-information</required-tag2>
  <first-name>Russel</first-name>
  <last-name>Jhonny</last-name>
  <licenses>
     <license>
        <number>840790</number>
        <state xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">CA</state>
        <field xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">Health</field>
     </license>
  </licenses>
</Person>
</People>

我只是不知道出了什么问题,因为当我从 xml 文件中删除最后两个人的信息并使用相同的 XSLT 对其进行测试时,它可以完美运行。而且我不知道如何删除匹配所有许可证的人的信息。

【问题讨论】:

  • @michael.hor257k 您好,先生,我是提出这个问题的人,我想我应该问一个新问题,因为在处理更大的 XML 文件时解决方案失败了。您能否指导我解决解决方案失败的原因?
  • 你说的更大是什么意思?是否真的是文件的大小,或者是否包含导致 XSLT 失败的 small 文件中没有的其他特征。根据我的经验,XSLT 不太可能因大小而失败。您是否尝试逐步增加尺寸以找到“罪魁祸首”?
  • @MarcusRickert 感谢您的及时回复,我的意思是文件中的更多信息。上面的 xml 文件包含 4 个人信息,如果我使用 2 个人信息,xslt 工作完美。我已将此作为测试文件。但是我真的很想改造一个4000人的信息文件。
  • @MarcusRickert 我试图逐步增加文件信息,但它在工作信息上失败了。就像我只使用一个人信息作品,两个人信息作品,当我使用3个人信息时,第一人称转换是错误的,第二人称是预期的,第三人称是错误的,我无法找到罪魁祸首。

标签: xml xslt xpath xslt-1.0


【解决方案1】:

一个明显的问题:

'state=//licensed-states/state' 将检查文档中的所有states,而不仅仅是特定于该用户的那些。与其从根目录搜索整个文档(这是路径前面的 // 所做的),不如给出从该状态到您要检查的区域的相对路径。至少,你需要说你只在同一个 Person 中寻找:

<xsl:template match="license[state=ancestor::Person//licensed-states/state]"/>

更快的性能是更明确地给出相对路径:

<xsl:template match="license[state=ancestor::Person/appointments/appointment-info/licensed-states/state]"/>

或者,由于您知道 Person 比许可证高两级,

<xsl:template match="license[state=../../appointments/appointment-info/licensed-states/state]"/>

其中..parent::* 的简写。

【讨论】:

  • 感谢您的及时回复。我是 XSLT 的新手,我不知道 // 会搜索整个文档。使用您对使用更快性能的建议,我能够实现我正在寻找的东西,但我也得到了拥有所有与 Ex 匹配的许可证的人。 我怎么能不包括它们?我只想保留匹配且具有非空许可证标签的人。非常感谢你帮助我。感谢所有帮助。
【解决方案2】:

如果您想在 XML 的另一部分中查找项目,请考虑使用 xsl:key 来执行此操作。在您的情况下,您想查找一个人的许可状态。这需要更多的努力,因为您需要使用连接键,由 Personstate 值的唯一标识符组成

<xsl:key name="state" match="licensed-states/state" use="concat(generate-id(ancestor::Person), '|', .)" /

generate-id() 是一个为节点生成唯一 ID 的函数。 (如果 XML 中有 Person 的一些 'id' 属性或元素,您可以使用它来代替)。

现在,您要排除所有州都有约会的人。为此,您需要将其设为双重否定,并排除所有没有处于约会状态的人

<xsl:template match="Person[not(licenses/license[not(key('state', concat(generate-id(ancestor::Person), '|', state)))])]"/>

排除一个州的许可证稍微简单一些

<xsl:template match="license[key('state', concat(generate-id(ancestor::Person), '|', state))]"/>

试试这个 XSLT

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

<xsl:key name="state" match="licensed-states/state" use="concat(generate-id(ancestor::Person), '|', .)" />

<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="Person[not(licenses/license[not(key('state', concat(generate-id(ancestor::Person), '|', state)))])]"/>

<xsl:template match="license[key('state', concat(generate-id(ancestor::Person), '|', state))]"/>

<xsl:template match="appointments" />

</xsl:stylesheet>

还要注意我如何删除了特定标签的xsl:apply-templates,例如firstname,而是使用&lt;xsl:template match="appointments" /&gt; 排除appointments,因此Person 的所有子节点都被复制,除了appointments

【讨论】:

    【解决方案3】:
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <!--Identity transform (aka identity template). This will match
    and copy attributes and nodes (element, text, comment and
    processing-instruction) without changing them. Unless a more
    specific template matches, everything will get handled by this
    template.-->    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <!--This template will match the "Person" element node. The "xsl:copy"
    creates the new "Person" element. The "xsl:apply-templates" tells
    the processor to apply templates to any attributes (of Person) or
    elements listed in the "select". (Other elements will not be 
    processed.) I used the union operator in the "select" so I wouldn't
    have to write multiple "xsl:apply-templates".-->
    <xsl:template match="Person">
        <xsl:copy>
            <xsl:apply-templates select="@*|first-name|last-name|
                required-tag1|required-tag2|licenses"/>
        </xsl:copy>
    </xsl:template>
    
    <!--This template will match any "license" element nodes that have a child 
    "state" element whose value matches a "state" element node that is a 
    child of "licensed-states". 
    This template will also match the "Person" element node if the number of
    "state" elements that don't have a corresponding "licensed-state"
    is equal to zero. ("filtered person who matched all licenses"
    requirement.)
    Since the "xsl:template" is empty, nothing 
    is output or processed further.-->
    <xsl:template match="license[state=../..//licensed-states/state]|
    Person[count(licenses/license[not(state=../..//licensed-states/state)])=0]"/>
    
    </xsl:stylesheet>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-14
      • 2015-10-14
      • 2019-01-15
      • 1970-01-01
      • 2021-09-19
      • 1970-01-01
      • 2019-04-11
      相关资源
      最近更新 更多