【问题标题】:Exclude certain child nodes when data structure is unknown数据结构未知时排除某些子节点
【发布时间】:2012-06-29 07:51:59
【问题描述】:

编辑 - 我已经找到了解决问题的方法并发布了问答here

我希望处理符合美国国会图书馆 EAD 标准(找到 here)的 XML。不幸的是,该标准在 XML 的结构方面非常松散。

例如,<bioghist> 标记可以存在于<archdesc> 标记中,也可以存在于<descgrp> 标记中,或者嵌套在另一个<bioghist> 标记中,或者是上述的组合,或者可以完全省略。我发现很难只选择我正在寻找的传记标签而不选择其他标签。

以下是我的 XSLT 可能需要处理的几个不同的 EAD XML 文档:

第一个例子

<ead>
<eadheader>
    <archdesc>
        <bioghist>one</bioghist>
        <dsc>
            <c01>
                <descgrp>
                    <bioghist>two</bioghist>
                </descgrp>
                <c02>
                    <descgrp>
                        <bioghist>
                            <bioghist>three</bioghist>
                        </bioghist>
                    </descgrp>
                </c02>
            </c01>
        </dsc>
    </archdesc>
</eadheader>
</ead>

第二个例子

<ead>
<eadheader>
    <archdesc>
        <descgrp>
            <bioghist>
                <bioghist>one</bioghist>
            </bioghist>
        </descgrp>
        <dsc>
            <c01>
                <c02>
                    <descgrp>
                        <bioghist>three</bioghist>
                    </descgrp>
                </c02>
                <bioghist>two</bioghist>
            </c01>
        </dsc>
    </archdesc>
</eadheader>
</ead>

第三个​​例子

<ead>
<eadheader>
    <archdesc>
        <descgrp>
            <bioghist>one</bioghist>
        </descgrp>
        <dsc>
            <c01>
                <c02>
                    <bioghist>three</bioghist>
                </c02>
            </c01>
        </dsc>
    </archdesc>
</eadheader>
</ead>

如您所见,EAD XML 文件可能几乎在任何地方都有&lt;bioghist&gt; 标签。我想产生的实际输出太复杂,无法在此处发布。上述三个 EAD 示例的输出简化示例可能如下:

第一个例子的输出

<records>
<primary_record>
    <biography_history>first</biography_history>
</primary_record>
<child_record>
    <biography_history>second</biography_history>
</child_record>
<granchild_record>
    <biography_history>third</biography_history>
</granchild_record>
</records>

第二个例子的输出

<records>
<primary_record>
    <biography_history>first</biography_history>
</primary_record>
<child_record>
    <biography_history>second</biography_history>
</child_record>
<granchild_record>
    <biography_history>third</biography_history>
</granchild_record>
</records>

第三个​​例子的输出

<records>
<primary_record>
    <biography_history>first</biography_history>
</primary_record>
<child_record>
    <biography_history></biography_history>
</child_record>
<granchild_record>
    <biography_history>third</biography_history>
</granchild_record>
</records>

如果我想提取“第一个”传记值并将其放入&lt;primary_record&gt;,我不能简单地使用&lt;xsl:apply-templates select="/ead/eadheader/archdesc/bioghist",因为该标签可能不是&lt;archdesc&gt; 标签的直接后代。它可能被&lt;descgrp&gt;&lt;bioghist&gt; 或它们的组合包裹。我不能select="//bioghist",因为这会拉出all &lt;bioghist&gt; 标签。我什至不能select="//bioghist[1]",因为那里实际上可能没有&lt;bioghist&gt; 标签,然后我将把值拉到&lt;c01&gt; 下面,这是“第二个”,应该稍后处理。

这已经是一篇很长的帖子,但另一个问题是&lt;cxx&gt; 节点的数量可以不受限制,最多嵌套十二层。我目前正在递归处理它们。我尝试将当前正在处理的节点(例如&lt;c01&gt;)保存为名为“RN”的变量,然后运行&lt;xsl:apply-templates select=".//bioghist [name(..)=name($RN) or name(../..)=name($RN)]"&gt;。这适用于某些形式的 EAD,其中 &lt;bioghist&gt; 标记没有嵌套太深,但如果它必须处理由喜欢将标记包装在其他标记中的人创建的 EAD 文件,它将失败(根据符合 EAD 标准)。

我喜欢用某种方式表达

  • 获取任何&lt;bioghist&gt;标签在当前节点下的任意位置,但
  • 如果您点击了&lt;c??&gt; 标签,请不要深入挖掘

我希望我已经说明了情况。如果我留下任何模棱两可的地方,请告诉我。您能提供的任何帮助将不胜感激。谢谢。

【问题讨论】:

    标签: xslt xpath xml-parsing


    【解决方案1】:

    由于要求相当模糊,任何答案仅反映其作者所做的猜测。

    这是我的:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:my="my:my" exclude-result-prefixes="my">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <my:names>
      <n>primary_record</n>
      <n>child_record</n>
      <n>grandchild_record</n>
     </my:names>
    
     <xsl:variable name="vNames" select="document('')/*/my:names/*"/>
    
     <xsl:template match="/">
      <xsl:apply-templates select=
       "//bioghist[following-sibling::node()[1]
                                    [self::descgrp]
                  ]"/>
     </xsl:template>
    
     <xsl:template match="bioghist">
      <xsl:variable name="vPos" select="position()"/>
    
      <xsl:element name="{$vNames[position() = $vPos]}">
       <xsl:value-of select="."/>
      </xsl:element>
     </xsl:template>
    
     <xsl:template match="text()"/>
    </xsl:stylesheet>
    

    当此转换应用于提供的 XML 文档时:

    <ead>
        <eadheader>
            <archdesc>
                <bioghist>first</bioghist>
                <descgrp>
                    <bioghist>first</bioghist>
                    <bioghist>
                        <bioghist>first</bioghist></bioghist>
                </descgrp>
                <dsc>
                    <c01>
                        <bioghist>second</bioghist>
                        <descgrp>
                            <bioghist>second</bioghist>
                            <bioghist>
                                <bioghist>second</bioghist></bioghist>
                        </descgrp>
                        <c02>
                            <bioghist>third</bioghist>
                            <descgrp>
                                <bioghist>third</bioghist>
                                <bioghist>
                                    <bioghist>third</bioghist></bioghist>
                            </descgrp>
                        </c02>
                    </c01>
                </dsc>
            </archdesc>
        </eadheader>
    </ead>
    

    产生想要的结果

    <primary_record>first</primary_record>
    <child_record>second</child_record>
    <grandchild_record>third</grandchild_record>
    

    【讨论】:

    • 对于模糊的要求,我深表歉意。一个适当的 EAD xml 文档包含 30 或 40 条不同的信息,每条信息都有自己的标签。我生成的输出使用了所有这些不同的标签,我认为简化的输入/输出可能最适合传达问题的性质。您的 xslt 比我熟悉的要高级一些,但我想我已经弄清楚了一些。匹配 bioghist 的模板只会运行 3 次,每次创建一个不同名称的元素,对吗?现在我的问题是为什么模板只运行 3 次。
    • @aarondev:答案很简单:提供的 XML 文档中只有三个元素与模板匹配。该模板匹配 XML 文档中的任何 bioghist,其第一个后续兄弟节点是 descgrp 元素——在提供的 XML 文档中恰好有三个这样的 bioghist 元素。
    • 因此,following-siblings 匹配所有兄弟节点。然后你只选择带有 [1] 的同胞中的第一个,对吗? self::descgrp 位仍然让我感到困惑。这是否使当前节点成为 descgrp 节点?
    • @aarondev:请看我之前的评论。 [self::descgrp] 是测试上下文节点是否为descgrp 元素。
    • 啊!这就是 self::descgrp 标签的作用。我现在明白了。谢谢!不幸的是,我似乎在描述我的问题方面做得很差。你已经解决了问题,但我仍然没有我正在寻找的解决方案。我现在该怎么办?将此答案标记为正确并提出更好的问题版本?以某种方式更新这个问题(使用实际上是更新的“答案”)?
    【解决方案2】:

    我自己制定了一个解决方案并将其发布在Q&A,因为该解决方案非常特定于某个 XML 标准,并且似乎超出了这个问题的范围。如果人们认为最好也将其发布在这里,我可以使用副本更新此答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多