XSLT - problems with changing node context in conjunction with an identity pattern

I have a source XML document with a structure like this:

<?xml version="1.0" encoding="UTF-8"?> <sample> <definition> <variable> <name>object01_ID_138368350261919620</name> <value>NUL</value> </variable> <variable> <name>param01_ID_138368350261919621</name> <value>10</value> </variable> <variable> <name>param02_ID_138368350261919622</name> <value>100</value> </variable> </definition> <override> <assignment> <name>object01_ID_138368350261919620</name> <path>module01/object01</path> </assignment> <assignment> <name>param01_ID_138368350261919621</name> <path>module01/object01/param01</path> </assignment> <assignment> <name>param02_ID_138368350261919622</name> <path>module01/object01/param02</path> </assignment> </override> </sample> 

Source XML Feature:
Each <assignment> element in the <override> element corresponds to exactly one <variable> element inside the <definition> element. This 1: 1 relationship is established by the contents of their <name> element.

Conversion and target XML requirements:
Depending on the structure of the content of the <path> elements in the <assignment> elements in the <override> element, I would like to add a new <assignment> element. It is important to note that the <assignment> element is the leading information. Therefore, always first you need to create a new <assignment> element with its contents <path> and <name> and in conjunction with it the corresponding new <variable> element with the same contents <name> and specific <value> content must be created and inserted The last position in the <definition> element. For example, to add param03, the correct result would look like this:

 <?xml version="1.0" encoding="UTF-8"?> <sample> <definition> <variable> <name>param00_ID_138368350261919620</name> <value>NUL</value> </variable> <variable> <name>param01_ID_138368350261919621</name> <value>10</value> </variable> <variable> <name>param02_ID_138368350261919622</name> <value>100</value> </variable> <variable> <name>Param03_ID_138368350261919623</name> <value>1000</value> </variable> </definition> <override> <assignment> <name>param00_ID_138368350261919620</name> <path>module01/object01</path> </assignment> <assignment> <name>param01_ID_138368350261919621</name> <path>module01/object01/param01</path> </assignment> <assignment> <name>param02_ID_138368350261919622</name> <path>module01/object01/param02</path> </assignment> <assignment> <name>Param03_ID_138368350261919623</name> <xpath>module01/object01/param03</xpath> </assignment> </override> </sample> 

XSL 2.0 style sheet for conversion:
To transform identity, I decided to use the fine-graded control rule recommended by [Dimitre Novatchev] . Using the param03 processing template, I create a new <assignment> element with my specific content <path> and <name> . Inside this template, I like to change the node context with for-each to a <definition> element and add a new <variable> element with the corresponding <name> content and the specific <value> content to the last position. This stylesheet has been tested with Saxon HE 9.5.

 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="fo xs fn"> <!-- global declarations ========================================================== --> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <!-- randomid here is just a fake for sake of simplification --> <xsl:variable name="randomid" select="138368350261919623"/> <!-- template - identity ========================================================== --> <xsl:template match="node()|@*" name="identity"> <xsl:copy> <xsl:apply-templates select="@*|node()[1]"/> </xsl:copy> <xsl:apply-templates select="following-sibling::node()[1]"/> </xsl:template> <!-- template - variable assignment =============================================== --> <xsl:template name="variable_assignment"> <xsl:param name="value_node_name"/> <xsl:param name="value_node_path"/> <xsl:message select="'processing: variable assignment'"/> <xsl:message select="concat('applying name: ', $value_node_name)"/> <xsl:message select="concat('applying path: ', $value_node_path)"/> <xsl:call-template name="identity"/> <assignment> <name> <xsl:value-of select="$value_node_name"/> </name> <xpath> <xsl:value-of select="$value_node_path"/> </xpath> </assignment> </xsl:template> <!-- template - processing param03 ============================================= --> <xsl:template match="/sample/override[not(assignment /path[matches(text(), '.*/object01/param03$')])] /assignment[path[matches(text(), '.*/object01$')]]"> <!-- setting params --> <xsl:param name="value_node_name_target"> <xsl:value-of select="concat('Param03_ID', '_', $randomid)"/> </xsl:param> <xsl:param name="value_node_path_target"> <xsl:value-of select="concat(./path, '/param03')"/> </xsl:param> <xsl:param name="value_node_value_target" select="'1000'"/> <!-- processing variable assignment --> <xsl:call-template name="variable_assignment"> <xsl:with-param name="value_node_name" select="$value_node_name_target"/> <xsl:with-param name="value_node_path" select="$value_node_path_target"/> </xsl:call-template> <!-- processing variable definition --> <xsl:for-each select="/sample/definition/*[position()=last()]"> <xsl:message select="'processing: variable definition'"/> <xsl:message select="concat('Here we are: ', .)"/> <xsl:message select="concat('applying name: ', $value_node_name_target)"/> <xsl:message select="concat('applying value: ', $value_node_value_target)"/> <xsl:call-template name="identity"/> <variable> <name> <xsl:value-of select="$value_node_name_target"/> </name> <value> <xsl:value-of select="$value_node_value_target"/> </value> </variable> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

The result is invalid XML:

 <?xml version="1.0" encoding="UTF-8"?> <sample> <definition> <variable> <name>object01_ID_138368350261919620</name> <value>NUL</value> </variable> <variable> <name>param01_ID_138368350261919621</name> <value>10</value> </variable> <variable> <name>param02_ID_138368350261919622</name> <value>100</value> </variable> </definition> <override> <assignment> <name>object01_ID_138368350261919620</name> <path>module01/object01</path> </assignment> <assignment> <name>param01_ID_138368350261919621</name> <path>module01/object01/param01</path> </assignment> <assignment> <name>param02_ID_138368350261919622</name> <path>module01/object01/param02</path> </assignment> <assignment> <name>Param03_ID_138368350261919623</name> <xpath>module01/object01/param03</xpath> </assignment> <variable> <name>param02_ID_138368350261919622</name> <value>100</value> </variable> <variable> <name>Param03_ID_138368350261919623</name> <value>1000</value> </variable> </override> </sample> 

I have problems:

  • The context of the node does not change. The new <variable> element is added at the last position to the <override> element, and not to the <definition> element, if required.
  • In addition, the last <variable> element from the <definition> element is copied to the <override> element. This is not what I want.

Help needed!
I would really appreciate it if someone could advise me how I would have to adapt my XSLT to get rid of the problems and the correct behavior, as described above.

Thank you very much.

Your proposed XSLT 2.0, adapted by me:

 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="fo xs fn"> <!-- global declarations ========================================================== --> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <!-- baserandom here is just a fake for sake of simplification --> <xsl:param name="baserandom" select="138368350261919623"/> <!--MOVED PARAMS FROM ORIGINAL TEMPLATE HERE SO THEY CAN BE USED BY MULTIPLE TEMPLATES --> <!--xsl:param name="value_node_path"--> <!--I LEFT THE PREDICATE BECAUSE IT APPEARS THAT THERE COULD BE MORE THAN ONE override ELEMENT.--> <!--xsl:value-of select="concat(/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])] /assignment[1]/path, '/param03')"/> </xsl:param> <xsl:param name="value_node_value" select="'1000'"/--> <!-- template - identity ========================================================== --> <xsl:template match="node()|@*" name="identity"> <xsl:copy> <xsl:apply-templates select="@*|node()[1]"/> </xsl:copy> <xsl:apply-templates select="following-sibling::node()[1]"/> </xsl:template> <!-- template - definition ======================================================== --> <!--REPLACES THE xsl:for-each THAT PROCESSES THE VARIABLE DEFINITION--> <xsl:template match="definition/*[last()]"> <xsl:param name="value_node_name"/> <xsl:param name="value_node_value"/> <xsl:call-template name="identity"/> <xsl:if test="$value_node_name"> <xsl:message select="'processing: variable definition'"/> <xsl:message select="concat('Here we are: ', .)"/> <xsl:message select="concat('applying name: ', $value_node_name)"/> <xsl:message select="concat('applying value: ', $value_node_value)"/> <variable> <name> <xsl:value-of select="$value_node_name"/> </name> <value> <xsl:value-of select="$value_node_value"/> </value> </variable> </xsl:if> </xsl:template> <!-- template - processing param03 ============================================= --> <xsl:template match="/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])] /assignment[path[matches(text(), '.*/object01$')]]"> <!-- name --> <xsl:param name="value_node_name"> <xsl:value-of select="concat('param03_ID', '_', $baserandom)"/> </xsl:param> <!-- path --> <xsl:param name="value_node_path"> <xsl:value-of select="concat(./path, '/param03')"/> </xsl:param> <!-- value --> <xsl:param name="value_node_value" select="'1000'"/> <!-- processing definition --> <xsl:apply-templates select="/sample/definition/*[last()]"> <xsl:with-param name="value_node_name" select="$value_node_name"/> <xsl:with-param name="value_node_value" select="$value_node_value"/> </xsl:apply-templates> <!-- processing assignment --> <xsl:message select="'processing: variable assignment'"/> <xsl:message select="concat('applying name: ', $value_node_name)"/> <xsl:message select="concat('applying path: ', $value_node_path)"/> <xsl:call-template name="identity"/> <assignment> <name> <xsl:value-of select="$value_node_name"/> </name> <path> <xsl:value-of select="$value_node_path"/> </path> </assignment> </xsl:template> </xsl:stylesheet> 

The resulting XML (still not so):

 <?xml version="1.0" encoding="UTF-8"?> <sample> <definition> <variable> <name>object01_ID_138368350261919620</name> <value>NUL</value> </variable> <variable> <name>param01_ID_138368350261919621</name> <value>10</value> </variable> <variable> <name>param02_ID_138368350261919622</name> <value>100</value> </variable> </definition> <override> <variable> <name>param02_ID_138368350261919622</name> <value>100</value> </variable> <variable> <name>param03_ID_138368350261919623</name> <value>1000</value> </variable> <assignment> <name>object01_ID_138368350261919620</name> <path>module01/object01</path> </assignment> <assignment> <name>param01_ID_138368350261919621</name> <path>module01/object01/param01</path> </assignment> <assignment> <name>param02_ID_138368350261919622</name> <path>module01/object01/param02</path> </assignment> <assignment> <name>param03_ID_138368350261919623</name> <path>module01/object01/param03</path> </assignment> </override> </sample> 
+6
source share
1 answer

The problem you are facing is that xsl:for-each does not change the output context. This changes the iterative context.

You still output in the context of assignment (pattern matching) when you repeat xsl:for-each select="/sample/definition/*[position()=last()] .

You will need to infer the new variable from the definition context.

I modified my XSLT to create what you want. This may not be the final decision, but it should be closer. I added comments (all in uppercase) to try to explain what I changed. Please let me know if you have any questions.

Modified XSLT 2.0

 <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="fo xs fn"> <!-- global declarations ========================================================== --> <xsl:output indent="yes"/> <xsl:strip-space elements="*"/> <!-- randomid here is just a fake for sake of simplification --> <xsl:param name="randomid" select="138368350261919623"/> <!--MOVED PARAMS FROM ORIGINAL TEMPLATE HERE SO THEY CAN BE USED BY MULTIPLE TEMPLATES --> <xsl:param name="value_node_name_target"> <xsl:value-of select="concat('Param03_ID', '_', $randomid)"/> </xsl:param> <xsl:param name="value_node_path_target"> <!--I LEFT THE PREDICATE BECAUSE IT APPEARS THAT THERE COULD BE MORE THAN ONE override ELEMENT.--> <xsl:value-of select="concat(/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])] /assignment[1]/path, '/param03')"/> </xsl:param> <xsl:param name="value_node_value_target" select="'1000'"/> <!-- template - identity ========================================================== --> <xsl:template match="node()|@*" name="identity"> <xsl:copy> <xsl:apply-templates select="@*|node()[1]"/> </xsl:copy> <xsl:apply-templates select="following-sibling::node()[1]"/> </xsl:template> <!-- template - variable assignment =============================================== --> <!--MOVED CODE FROM THIS TEMPLATE INTO THE assignment TEMPLATE--> <!--REPLACES THE xsl:for-each THAT PROCESSES THE VARIABLE DEFINITION--> <xsl:template match="definition[../override[not(assignment/path[matches(text(), '.*/object01/param03$')])] /assignment[path[matches(text(), '.*/object01$')]]]/*[last()]"> <xsl:message select="'processing: variable definition'"/> <xsl:message select="concat('Here we are: ', .)"/> <xsl:message select="concat('applying name: ', $value_node_name_target)"/> <xsl:message select="concat('applying value: ', $value_node_value_target)"/> <xsl:call-template name="identity"/> <variable> <name> <xsl:value-of select="$value_node_name_target"/> </name> <value> <xsl:value-of select="$value_node_value_target"/> </value> </variable> </xsl:template> <!-- template - processing param03 ============================================= --> <xsl:template match="/sample/override[not(assignment/path[matches(text(), '.*/object01/param03$')])] /assignment[path[matches(text(), '.*/object01$')]]"> <!-- setting params --> <!--MOVED TEMPLATE PARAMS TO GLOBAL PARAMS--> <!-- processing variable assignment --> <!--REPLACED UNNECESSARY xsl:call-template WITH ACTUAL CODE--> <xsl:message select="'processing: variable assignment'"/> <xsl:message select="concat('applying name: ', $value_node_name_target)"/> <xsl:message select="concat('applying path: ', $value_node_path_target)"/> <xsl:call-template name="identity"/> <assignment> <name> <xsl:value-of select="$value_node_name_target"/> </name> <!--CHANGED FROM xpath TO path (APPEARED TO BE A TYPO)--> <path> <xsl:value-of select="$value_node_path_target"/> </path> </assignment> <!-- processing variable definition --> <!--THIS IS NOW DONE BY A SEPARATE MATCHING TEMPLATE--> </xsl:template> </xsl:stylesheet> 

Output

 <sample> <definition> <variable> <name>object01_ID_138368350261919620</name> <value>NUL</value> </variable> <variable> <name>param01_ID_138368350261919621</name> <value>10</value> </variable> <variable> <name>param02_ID_138368350261919622</name> <value>100</value> </variable> <variable> <name>Param03_ID_138368350261919623</name> <value>1000</value> </variable> </definition> <override> <assignment> <name>object01_ID_138368350261919620</name> <path>module01/object01</path> </assignment> <assignment> <name>param01_ID_138368350261919621</name> <path>module01/object01/param01</path> </assignment> <assignment> <name>param02_ID_138368350261919622</name> <path>module01/object01/param02</path> </assignment> <assignment> <name>Param03_ID_138368350261919623</name> <path>module01/object01/param03</path> </assignment> </override> </sample> 
+1
source

Source: https://habr.com/ru/post/958569/


All Articles