【问题标题】:filtering non referenced member nodes using XSLT使用 XSLT 过滤非引用的成员节点
【发布时间】:2012-05-23 08:07:09
【问题描述】:

OpenStreetMap xml 文档由一组“node”元素和一组“way”元素组成(除其他外)。

“节点”元素可以(可选)嵌套“标签”元素。

“way”元素由“node”元素的有序列表组成,由嵌套元素“nd”引用,它们的属性“ref”指向“node”元素的属性“id”。

这里是一个例子:

<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="CGImap 0.0.2">
  <node id="1726631203" lat="50.8500083" lon="4.3553223" visible="true" version="6" changeset="9938190" timestamp="2011-11-24T22:05:32Z"/>
  ...
  <way id="160611697" user="toSc" uid="246723" visible="true" version="1" changeset="11385198" timestamp="2012-04-22T14:57:19Z">
    <nd ref="1726631203"/>
    <nd ref="1726631223"/>
    <nd ref="1726631213"/>
    <nd ref="1726631205"/>
    <nd ref="1726631185"/>
    <nd ref="1726631203"/>
  </way>
  ...
</osm>

我的问题是如何使用 XSLT 进行以下转换?

  • 过滤所有未被任何方式元素引用的节点元素。
  • 过滤引用未包含在源 xml 文档中的节点元素的方式元素。
  • 将属性“visible”更改为“false”,更改为任何没有“tag”子元素的“node”元素。

任何其他元素都应保留在生成的 xml 中。

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    我的问题是如何使用 XSLT 执行以下操作 转型?

    • 过滤所有未被任何方式元素引用的节点元素。

    • 过滤引用未包含在源 xml 文档中的节点元素的方式元素。

    • 将属性“visible”更改为“false”,更改为没有“tag”子元素的任何“node”元素。

    此转换满足所有三个要求:

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:key name="kND-By-Ref" match="way/nd" use="@ref"/>
     <xsl:key name="kNodeById" match="node" use="@id"/>
    
     <xsl:template match="node()|@*">
         <xsl:copy>
           <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
     </xsl:template>
    
     <xsl:template match="node[not(key('kND-By-Ref', @id))]"/>
     <xsl:template match="way[nd[not(key('kNodeById', @ref))]]"/>
    
     <xsl:template match="node[not(tag)]/@visible">
      <xsl:attribute name="visible">false</xsl:attribute>
     </xsl:template>
    </xsl:stylesheet>
    

    应用于此 XML 文档时(适合创建以包含每个要求的案例):

    <osm version="0.6" generator="CGImap 0.0.2">
        <node id="1726631203" lat="50.8500083" lon="4.3553223" visible="true"
          version="6" changeset="9938190" timestamp="2011-11-24T22:05:32Z">
          <tag/>
     </node>
        <node id="1726631223" lat="50.8500083" lon="4.3553223" visible="true"
          version="6" changeset="9938190" timestamp="2011-11-24T22:05:32Z"/>
        <node id="ZZZZZZZ" lat="50.8500083" lon="4.3553223" visible="true"
          version="6" changeset="9938190" timestamp="2011-11-24T22:05:32Z"/>
        <way id="160611697" user="toSc" uid="246723" visible="true"
          version="1" changeset="11385198" timestamp="2012-04-22T14:57:19Z">
            <nd ref="1726631203"/>
            <nd ref="1726631223"/>
        </way>
        <way id="160611698" user="toSc" uid="246723" visible="true"
          version="1" changeset="11385198" timestamp="2012-04-22T14:57:19Z">
            <nd ref="1726631203"/>
            <nd ref="1726631223"/>
            <nd ref="1726631213"/>
            <nd ref="1726631205"/>
            <nd ref="1726631185"/>
            <nd ref="1726631203"/>
        </way>
    </osm>
    

    想要的正确结果(执行所有过滤,node 元素之一的visible 属性变为false产生

    <osm version="0.6" generator="CGImap 0.0.2">
       <node id="1726631203" lat="50.8500083" lon="4.3553223"
        visible="true" version="6" changeset="9938190" timestamp="2011-11-24T22:05:32Z">
          <tag/>
       </node>
       <node id="1726631223" lat="50.8500083" lon="4.3553223"
        visible="false" version="6" changeset="9938190" timestamp="2011-11-24T22:05:32Z"/>
       <way id="160611697" user="toSc" uid="246723" visible="true"
        version="1" changeset="11385198" timestamp="2012-04-22T14:57:19Z">
          <nd ref="1726631203"/>
          <nd ref="1726631223"/>
       </way>
    </osm>
    

    解释

    1. 身份规则被三个模板覆盖,每个模板实现三个要求之一。

    2. 具有空主体的两个覆盖模板实现了两个过滤要求。

    3. 我们正在使用keys通过id属性和nd通过ref属性方便高效地找到node

    4. 属性值替换要求在第三个覆盖模板中实现。

    【讨论】:

    • 感谢@Dimitre 提供如此完整的答案!。只是想了解一下这个逻辑:在声明键时,为什么在匹配条件中写“way/nd”和“node”而不是“/osm/way/nd”和“/osm/node”?,我的问题是我不知道这一行上“key”函数的当前上下文路径在哪里: 换句话说,在我看来,关键函数中的路径应该是绝对的(正如我上面写的)或在节点元素的上下文中,因为它是由“匹配”条件匹配的元素。
    • @Sergio:match 属性中的表达式是一个“匹配模式”,通常是一个简短的相对 XPath 表达式。只有在需要消歧时才需要指定祖先。在这种情况下,我可以只使用match="nd",因为nd 元素仅作为way 的子元素出现。如果他们可能是 2 个不同名字的父母的孩子,则需要 way/ndmatch="nd" 表示(与在xsl:template 上指定的mtch 模式完全相同:匹配当前节点的任何子节点,其(子节点的)名称为"nd"。您需要阅读有关匹配的信息模式以更好地理解它们。
    • 我明白了,在最后一个模板中,你为什么写
    • @Sergio:我这样做是因为我想匹配属性本身——而不是包含它的元素,身份模板负责处理元素——唯一必须更改的节点是属性。
    【解决方案2】:

    这样的事情应该可以工作:

    <xsl:for-each select="//osm/node">
       <xsl:if test="//osm/way/nd[@ref=current()/@id]">
    
         <xsl:copy-of select=".">
    
       </xsl:if>
    </xsl:for-each>
    

    诀窍是如果 xpath 表达式返回一个或多个结果,则 if 节点中的测试返回 true。您可以使用相同的技术来检查 tag 属性是否存在。不幸的是,当您需要更改属性时,您需要手动复制其他属性(xsl:复制除 visible 属性之外的所有子元素,而不是简单地使用 xsl:copy-of)。

    【讨论】:

      【解决方案3】:

      这似乎有效:

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      
        <xsl:template match="node">
          <xsl:if test="//osm/way/nd[@ref=current()/@id]">
            <xsl:choose>
              <xsl:when test="tag">
                <xsl:copy-of select="."/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:copy>
                  <xsl:copy-of select="@*[not(local-name(.) = 'visible')]"/>
                  <xsl:attribute name="visible">false</xsl:attribute>
                </xsl:copy>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:if>
        </xsl:template>
      
      </xsl:stylesheet>
      

      xsl:if 测试是从上一个答案中复制的,并消除了未引用的节点。

      当标签子项不存在时,xsl:choose 中的位将可见属性设置为 false。这是一种更改属性值的方法, 不必单独复制所有属性;但它会忽略标签以外的任何子元素。

      我还没有解决方式元素的过滤问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-25
        • 1970-01-01
        • 2019-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多