4

我正在努力寻找最好(有效)的方式来做到这一点。

我有一个中等大小的 XML 文档。根据特定设置,出于安全原因,需要过滤掉其中的某些部分。

我将在 XSLT 中执行此操作,因为它是可配置的,并且不需要更改任何代码。

我环顾四周,但运气不佳。

例如:

我有以下 XPath:

//*[@root='2.16.840.1.113883.3.51.1.1.6.1']

Whicrooth 为我提供了根属性等于特定 OID 的所有节点。在这些节点中,我希望删除除少数(例如foobar)之外的所有属性,然后添加另一个属性(例如reason

对于具有特定属性的节点,我还需要有多个 XPath 表达式,它们可以在特定节点上运行到零,并以类似的方式清除它的内容。

我正在玩弄来自以下方面的信息:

XPath 表达式选择除特定列表之外的所有 XML 子节点?

并按XSL 参数的名称删除元素和/或属性

当我可以访问我到目前为止所做的事情时,将很快更新。

例子:

转换前的 XML。 更新:我想过滤掉扩展,然后是文档中与该扩展属性的值匹配的所有值:

<root>
    <childNode>
        <innerChild root="2.16.840.1.113883.3.51.1.1.6.1" extension="123" type="innerChildness"/>
        <innerChildSibling/>
    </childNode>
    <animals>
     <cat>
       <name>123</name>
     </cat>
    </animals>
    <tree/>
    <water root="2.16.840.1.113883.3.51.1.1.6.1" extension="1223" type="liquidLIke"/>
</root>

<root>
    <childNode>
        <innerChild root="2.16.840.1.113883.3.51.1.1.6.1" flavor="MSK"/> <!-- filtered -->
        <innerChildSibling/>
    </childNode>
    <animals>
      <cat>
        <name>****</name>
       </cat> <!-- cat was filtered -->
    </animals>
    <tree/>
    <water root="2.16.840.1.113883.3.51.1.1.6.1" flavor="MSK"/> <!-- filtered -->
</root>

我能够使用 XSLT2。

我正在尝试这个没有任何运气(对于初学者)

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

    <xsl:param name="OIDAttrToDelete" select="'extension'"/>

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

    <!-- Get all nodes for the OID -->
    <xsl:template match="//*[@root='2.16.840.1.113883.3.51.1.1.6.1']">
        <xsl:if test="name() = $OIDAttrToDelete">
            <xsl:attribute name="nullFlavor">MSK</xsl:attribute>
            <xsl:call-template name="identity"/>            
        </xsl:if>
    </xsl:template>    
</xsl:stylesheet>
4

2 回答 2

2

这是一个完整的 XSLT 2.0 转换,它根据外部参数识别具有特定属性名称和值的元素,并为每个此类元素删除所有未列入白名单的属性并添加其他指定属性

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="vFilters">
     <filter>
      <markerAttribute name="root">2.16.840.1.113883.3.51.1.1.6.1</markerAttribute>
      <whiteListedAttributes>
        <name>root</name>
        <name>foo</name>
      </whiteListedAttributes>
      <addAtributes flavor="MSK" reason="Demo"/>
     </filter>
 </xsl:param>

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

 <xsl:template match=
 "*[for $cur in .,
        $m in $vFilters/filter/markerAttribute
     return
        $cur/@*[name() eq $m/@name and . eq $m]
   ]">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:copy-of select=
     "for $m
           in $vFilters/filter/markerAttribute
       return
         if(current()/@*
                      [name() eq $m/@name
                     and
                      . eq $m
                      ])
           then
             $m/../addAtributes/@*
           else ()
     "/>
    <xsl:apply-templates/>
  </xsl:copy>
 </xsl:template>

  <xsl:template match=
 "@*[for $cur in .,
         $p in ..,
         $m in $vFilters/filter/markerAttribute
     return
          $p/@*[name() eq $m/@name and . eq $m]
         and
          not(name($cur) = $m/../whiteListedAttributes/name)
    ]
  "/>
</xsl:stylesheet>

当此转换应用于以下 XML 文档时(基于提供的,但添加了一个列入白名单的属性):

<root>
    <childNode>
        <innerChild root="2.16.840.1.113883.3.51.1.1.6.1"
          a="b" b="c" foo="bar" type="innerChildness"/>
        <innerChildSibling/>
    </childNode>
    <animals>
        <cat>
            <name>bob</name>
        </cat>
    </animals>
    <tree/>
    <water root="2.16.840.1.113883.3.51.1.1.6.1"
    z="zed" l="ell" type="liquidLIke"/>
</root>

产生了想要的、正确的结果——在已识别的元素上,所有非白名单属性都被删除,并添加了过滤器中指定的两个新属性:

<root>
      <childNode>
            <innerChild root="2.16.840.1.113883.3.51.1.1.6.1" foo="bar" flavor="MSK" reason="Demo"/>
            <innerChildSibling/>
      </childNode>
      <animals>
            <cat>
                  <name>bob</name>
            </cat>
      </animals>
      <tree/>
      <water root="2.16.840.1.113883.3.51.1.1.6.1" flavor="MSK" reason="Demo"/>
</root>

说明

外部参数$vFilters可以包含一个或多个过滤器,如下所示:

 <filter>
  <markerAttribute name="root">2.16.840.1.113883.3.51.1.1.6.1</markerAttribute>
  <whiteListedAttributes>
    <name>root</name>
    <name>foo</name>
  </whiteListedAttributes>
  <addAtributes flavor="MSK" reason="Demo"/>
 </filter>

markerAttribute元素指定标识属性的名称和值。root在这种情况下,过滤器标识(适用于)具有值为的属性的元素"2.16.840.1.113883.3.51.1.1.6.1"

此过滤器中指定了两个列入白名单的属性名称:rootfoo.

具有指定值的两个新属性将添加到此过滤器元素标识的每个属性上:flavor="MSK"reason="Demo"

外部参数$vFilters可以包含许多过滤器,每个过滤器标识不同的“类型”元素并指定一组不同的白名单属性名称和要添加的新属性。

于 2012-06-19T03:29:04.680 回答
2
<xsl:param name="OIDAttrToDelete" select="'extension'" />

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

<xsl:template match="@*">
  <xsl:choose>
    <xsl:when test="../@root = '2.16.840.1.113883.3.51.1.1.6.1'">
      <xsl:copy-of select=".[not(contains($OIDAttrToDelete, name()))]" />
    </xsl:when>
    <xsl:otherwise>
      <xsl:copy-of select=".">
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

笔记:

我创建了一个仅匹配属性并决定是否复制它们的模板。这样我就不必过多地干扰身份模板。

无需为身份模板命名。只需<apply-templates>使用适当的选择表达式调用,处理器就会自动调用它。

模板中的匹配表达式不是完整的 XPath 表达式。你不需要匹配//*[predicate]。使用*[predicate]就足够了。

如果您出于安全考虑,我会考虑使用白名单 ( $OIDAttrToKeep)。

如果$OIDAttrToDelete是值列表(例如逗号分隔),则应在测试中包含分隔符:

.[
  not(
    contains(
      concat(',', $OIDAttrToDelete, ','), 
      concat(',', name(), ',') 
    )
  )
]

以避免部分名称匹配。

如果您的父 OID 应该是可配置的,您可以使用相同的技术:

<xsl:template match="@*">
  <xsl:choose>
    <xsl:when test="
      contains(
        concat(',', $OIDToStrip, ','),
        concat(',', ../@root, ',')
      )
    ">
    <!-- ... -->
    </xsl:when>
  </xsl:choose>
</xsl:template>
于 2012-06-18T21:56:37.013 回答