如何使用XSLT基于指定XPath修改特定XML结构
XSLT Solution to Modify XML with Custom XPath-like Rules
Problem Context
This is a follow-up to your earlier Stack Overflow question about editing specific XML with XPath, but the current XML uses the http://www.w3.org/2005/xpath-functions namespace and a nested map/string/array structure—so the previous solution needs adjustments to work with this setup.
Original XML
<?xml version="1.0" encoding="UTF-8"?> <map xmlns="http://www.w3.org/2005/xpath-functions"> <map key="Request"> <map key="Headers"> <string key="Accept">application/json</string> <string key="Content-Type">application/json</string> </map> <map key="Payload"> <map key="root"> <array key="req1"> <string>puneet</string> <string>taneja</string> </array> <array key="req2"> <string>ratan</string> </array> </map> </map> </map> </map>
Custom XPath-like Rules
These are your target update rules (note: these aren't standard XPath, so we'll parse them in the XSLT):
/Request/Headers/Accept=app /Request/Headers/Content-Type=json /Request/Payload/root/req1[2]=singh /Request/Payload/root/req1[1]=pradeep /Request/Payload/root/req2=suman
Desired Updated XML
<?xml version="1.0" encoding="UTF-8"?> <map xmlns="http://www.w3.org/2005/xpath-functions"> <map key="Request"> <map key="Headers"> <string key="Accept">application/json</string> <string key="Content-Type">application/json</string> </map> <map key="Payload"> <map key="root"> <array key="req1"> <string>pradeep</string> <string>singh</string> </array> <array key="req2"> <string>suman</string> </array> </map> </map> </map> </map>
Adapted XSLT Solution
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://www.w3.org/2005/xpath-functions" exclude-result-prefixes="fn"> <!-- Parameter to hold your update rules --> <xsl:param name="update-rules"> <rule>/Request/Headers/Accept=app</rule> <rule>/Request/Headers/Content-Type=json</rule> <rule>/Request/Payload/root/req1[2]=singh</rule> <rule>/Request/Payload/root/req1[1]=pradeep</rule> <rule>/Request/Payload/root/req2=suman</rule> </xsl:param> <!-- Identity template: copy all nodes by default --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <!-- Template to update string nodes inside arrays or maps --> <xsl:template match="fn:string"> <!-- Generate the path of the current node to match your rule format --> <xsl:variable name="current-path"> <xsl:for-each select="ancestor-or-self::*[starts-with(local-name(), 'map') or starts-with(local-name(), 'array')]"> <xsl:if test="position() > 1">/</xsl:if> <xsl:value-of select="@key"/> <!-- Add index if parent is an array --> <xsl:if test="parent::fn:array"> <xsl:text>[</xsl:text> <xsl:value-of select="position()"/> <xsl:text>]</xsl:text> </xsl:if> </xsl:for-each> <!-- Append string key if directly under a map --> <xsl:if test="parent::fn:map"> <xsl:value-of select="concat('/', @key)"/> </xsl:if> </xsl:variable> <!-- Find matching rule for this path --> <xsl:variable name="matching-rule" select="$update-rules/rule[starts-with(., concat('/', $current-path, '='))]"/> <xsl:copy> <xsl:apply-templates select="@*"/> <!-- Use new value if rule exists, else keep original --> <xsl:choose> <xsl:when test="$matching-rule"> <xsl:value-of select="substring-after($matching-rule, '=')"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> </xsl:copy> </xsl:template> <!-- Special case: handle rules targeting single-element arrays directly --> <xsl:template match="fn:array[string]"> <xsl:variable name="array-key" select="@key"/> <xsl:variable name="parent-path"> <xsl:for-each select="ancestor::fn:map"> <xsl:if test="position() > 1">/</xsl:if> <xsl:value-of select="@key"/> </xsl:for-each> </xsl:variable> <xsl:variable name="rule-path" select="concat('/', $parent-path, '/', $array-key)"/> <xsl:variable name="matching-rule" select="$update-rules/rule[starts-with(., concat($rule-path, '='))]"/> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:choose> <xsl:when test="$matching-rule"> <string> <xsl:value-of select="substring-after($matching-rule, '=')"/> </string> </xsl:when> <xsl:otherwise> <xsl:apply-templates select="node()"/> </xsl:otherwise> </xsl:choose> </xsl:copy> </xsl:template> </xsl:stylesheet>
Key Adjustments Explained
- Namespace Handling: We declare the
fnprefix for thehttp://www.w3.org/2005/xpath-functionsnamespace to correctly target nodes in the XML. - Rule Parsing: The XSLT accepts your custom
path=valuerules as a parameter, then generates matching paths for each node to check against the rules. - Array Index Support: For elements inside
arraynodes, we generate paths with positional indices (likereq1[1]) to align with your rule format. - Single-Element Array Fix: We handle rules that target an array directly (e.g.,
/Request/Payload/root/req2=suman) by updating its single childstringnode.
内容的提问来源于stack exchange,提问作者VSS
相关产品推荐
相关产品推荐

