XSLT - remove nodes + attributes corresponding to Xpath

I am trying to find a better (efficient) way to do this.

I have a medium sized XML document. Depending on the specific settings, some parts must be filtered out for security reasons.

I will do this in XSLT as it is customizable and no code needs to be changed.

I looked around, but did not get much luck.

For instance:

I have the following XPath:

//*[@root='2.16.840.1.113883.3.51.1.1.6.1'] 

Whicrooth gives me all nodes with a root attribute equal to a specific OID. In these nodes, I want all the attributes, except for a few (for example, foo and bar ), to be deleted, and then another attribute added (example / strong>)

I also need to have several XPath expressions that can run to zero on a particular node and clear its contents in the same way for nodes with specific attributes.

I play with information from:

XPath expression to select all XML child nodes except a specific list?

and Remove elements and / or attributes by name for XSL options

It will be updated soon when I have access to what I have done so far.

Example:

XML before transformation. UPdate: I want to filter the extension, and then all the values ​​in the document that match the value of this extension attribute:

 <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> 

After

 <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> 

I can use XSLT2.

I tried this with no luck (for starters)

 <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
source share
2 answers
 <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> 

Notes:

I created a template that matches only attributes and decides whether to copy them or not. Thus, I should not interfere much with the identification pattern.

No name is required for the identifier pattern. Just call <apply-templates> with the appropriate select statement, and the processor will call it automatically.

Matching characters in templates are not full XPath expressions. You do not need to match //*[predicate] . Using *[predicate] enough.

If the security issue is your cause, I would consider a whitelist ( $OIDAttrToKeep ).

If $OIDAttrToDelete is a list of values ​​(for example, separated by a comma), you should include the separator in the test:

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

to avoid partial name matches.

If your parent OID needs to be custom, you can use the same method:

 <xsl:template match="@*"> <xsl:choose> <xsl:when test=" contains( concat(',', $OIDToStrip, ','), concat(',', ../@root, ',') ) "> <!-- ... --> </xsl:when> </xsl:choose> </xsl:template> 
+2
source

Here is the complete XSLT 2.0 transformation, which, in accordance with an external parameter, identifies elements that have a specific attribute name and value, and for each of these elements, all attributes that are not white are deleted and other specified attributes are added

 <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> 

When this conversion is applied to the following XML document (based on the provided but added one whitelisted attribute):

 <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> 

The obtained, correct result is obtained . In the identified elements, all attributes not included in the white list are deleted and two new attributes specified in the filter are added:

 <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> 

Explanation

The external parameter $vFilters may contain one or more filters in the following form:

  <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> 

The markerAttribute element indicates the name and value of the identification attribute. In this case, the filter identifies (for) elements that have a root attribute whose value is "2.16.840.1.113883.3.51.1.1.6.1" .

Two whitelisted attribute names are specified in this filter: root and foo .

Two new attributes with the specified values ​​must be added to each identified by this filter element: flavor="MSK" and reason="Demo" .

The external parameter $vFilters can contain many filters, each of which identifies a different "type" of the element and sets a different set of names for white list attributes and new attributes to be added.

+2
source

All Articles