如何使用Microsoft xslt 1.0有效地稍微修改大型xml文档
我想转换此xml:如何使用Microsoft xslt 1.0有效地稍微修改大型xml文档,xml,xslt-1.0,Xml,Xslt 1.0,我想转换此xml: <Root> <Result> <Message> <Header> <!-- Hundreds of child nodes --> </Header> <Body> <Node1> <!-- Hundreds of child nodes -->
<Root>
<Result>
<Message>
<Header>
<!-- Hundreds of child nodes -->
</Header>
<Body>
<Node1>
<!-- Hundreds of child nodes -->
<Node2>
<!-- Hundreds of child nodes -->
<Node3>
<!-- Hundreds of child nodes -->
<NodeX>value 1 to be changed</NodeX>
<!-- Hundreds of child nodes -->
<Node4>
<!-- Hundreds of child nodes -->
<NodeY>value 2 to be changed</NodeY>
</Node4>
</Node3>
</Node2>
</Node1>
</Body>
<RealValuesRoot>
<!-- These two nodes -->
<Value ID="1">this value must replace the value of Node X</Value>
<Value ID="2">this value must replace the value of Node Y</Value>
</RealValuesRoot>
</Message>
</Result>
<!-- Hundreds to thousands of similar MessageRoot nodes -->
</Root>
<Root>
<Result>
<Message>
<Header>
<!-- Hundreds of child nodes -->
</Header>
<Body>
<Node1>
<!-- Hundreds of child nodes -->
<Node2>
<!-- Hundreds of child nodes -->
<Node3>
<!-- Hundreds of child nodes -->
<NodeX>this value must replace the value of Node X</NodeX>
<!-- Hundreds of child nodes -->
<Node4>
<!-- Hundreds of child nodes -->
<NodeY>this value must replace the value of Node Y</NodeY>
</Node4>
</Node3>
</Node2>
</Node1>
</Body>
</Message>
</Result>
<!-- Hundreds to thousands of similar MessageRoot nodes -->
</Root>
要更改的值1
要更改的值2
此值必须替换节点X的值
此值必须替换节点Y的值
在此xml中:
<Root>
<Result>
<Message>
<Header>
<!-- Hundreds of child nodes -->
</Header>
<Body>
<Node1>
<!-- Hundreds of child nodes -->
<Node2>
<!-- Hundreds of child nodes -->
<Node3>
<!-- Hundreds of child nodes -->
<NodeX>value 1 to be changed</NodeX>
<!-- Hundreds of child nodes -->
<Node4>
<!-- Hundreds of child nodes -->
<NodeY>value 2 to be changed</NodeY>
</Node4>
</Node3>
</Node2>
</Node1>
</Body>
<RealValuesRoot>
<!-- These two nodes -->
<Value ID="1">this value must replace the value of Node X</Value>
<Value ID="2">this value must replace the value of Node Y</Value>
</RealValuesRoot>
</Message>
</Result>
<!-- Hundreds to thousands of similar MessageRoot nodes -->
</Root>
<Root>
<Result>
<Message>
<Header>
<!-- Hundreds of child nodes -->
</Header>
<Body>
<Node1>
<!-- Hundreds of child nodes -->
<Node2>
<!-- Hundreds of child nodes -->
<Node3>
<!-- Hundreds of child nodes -->
<NodeX>this value must replace the value of Node X</NodeX>
<!-- Hundreds of child nodes -->
<Node4>
<!-- Hundreds of child nodes -->
<NodeY>this value must replace the value of Node Y</NodeY>
</Node4>
</Node3>
</Node2>
</Node1>
</Body>
</Message>
</Result>
<!-- Hundreds to thousands of similar MessageRoot nodes -->
</Root>
此值必须替换节点X的值
此值必须替换节点Y的值
除了以下变化外,输出与输入几乎相同:
如前所述,我的xslt工作正常,但我希望尽可能提高性能,请随意推荐一个全新的xslt逻辑!欢迎您的意见和建议 最有效的方法是使用XSL键
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- a key that indexes real values by their IDs -->
<xsl:key name="kRealVal" match="RealValuesRoot/Value" use="@ID" />
<!-- the identity template to copy everything -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<!-- ...except elements named <NodeX> -->
<xsl:template match="*[starts-with(name(), 'Node')]">
<xsl:variable name="myID" select="substring-after(name(), 'Node')" />
<xsl:variable name="myRealVal" select="key('kRealVal', $myID)" />
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:choose>
<xsl:when test="$myRealVal">
<xsl:value-of select="$myRealVal" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="node()" />
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<!-- the <RealValuesRoot> element can be trashed -->
<xsl:template match="RealValuesRoot" />
</xsl:stylesheet>
以下是此解决方案的实时预览:
下面是一个概念验证解决方案,它使用Microsoft脚本扩展来完成繁重的工作:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:script="http://tempuri.org/script"
>
<msxsl:script language="JScript" implements-prefix="script">
var index = {};
function getNode(context, xpath) {
var theContext = context[0],
theXpath = xpath[0].text,
result;
try {
result = theContext.selectSingleNode(theXpath)
} catch (ex) {
// xpath is invalid. we could also just throw here
// but lets return the empty node set.
result = theContext.selectSingleNode("*[false()]");
}
return result;
}
function buildIndex(id, node) {
var theNode = node[0];
if (id) index[id] = theNode;
return "";
}
function getValue(id) {
return (id in index) ? index[id] : '';
}
</msxsl:script>
<!-- this is the boilerplate to evaluate all the XPaths -->
<xsl:variable name="temp">
<xsl:for-each select="/root/source/map">
<xsl:value-of select="script:buildIndex(generate-id(script:getNode(/, @xpath)), .)" />
</xsl:for-each>
</xsl:variable>
<!-- the identity template to get things rolling -->
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<!-- actually evaluate $temp once, so the variable is being calculated -->
<xsl:value-of select="$temp" />
<xsl:apply-templates select="node() | @*" />
</xsl:template>
<!-- all <value> nodes do either have a related "actual value" or they are copied as they are -->
<xsl:template match="value">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:variable name="newValue" select="script:getValue(generate-id())" />
<xsl:choose>
<xsl:when test="$newValue">
<xsl:value-of select="$newValue" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="node() | @*" />
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
<!-- the <source> element can be dropped -->
<xsl:template match="source" />
</xsl:stylesheet>
var指数={};
函数getNode(上下文,xpath){
var theContext=context[0],
xpath=xpath[0]。文本,
结果;
试一试{
结果=上下文。选择SingleNode(XPath)
}捕获(ex){
//xpath无效。我们也可以在这里抛出
//但是让我们返回空节点集。
结果=上下文。选择SingleNode(“*[false()]”);
}
返回结果;
}
函数buildIndex(id,节点){
var theNode=node[0];
如果(id)索引[id]=节点;
返回“”;
}
函数getValue(id){
返回(索引中的id)?索引[id]:“”;
}
它转变
<root>
<value id="foo">this is to be replaced</value>
<source>
<map xpath="/root/value[@id = 'foo']">this is the new value</map>
</source>
</root>
这是要更换的
这是新的值
到
这是新的值
也许你可以在你的设置中走这条路线
思路是这样的:
- 迭代所有XPath,使用
对其进行评估。选择SingleNode()
- 将每个评估结果(理想情况下为一个节点)及其唯一ID作为键值对存储在对象中。这使用XSLT的
从节点获取idgenerate-id()
- 现在正常转换输入。对于每个有问题的节点,获取其ID并检查该节点是否实际存在“新值”李>
- 如果有,则插入新值,如果没有,则继续转换
元素,但这部分并不是真正必要的,也不容易适应您的实际情况。例如,可以使用JavaScript中的split()
从一长串XPath构建index
对象
我计算当前节点的xpath,然后执行以下操作之一
这很可能是您的效率低下-如果您每次都在查看O(N2)算法时重新计算返回根的路径。在没有看到XSLT的情况下,这是一种推测,但是如果您的主要算法基于标准标识模板,那么您可以通过使用参数沿着递归传递当前路径来稍微调整它
<xsl:template match="@*|node()">
<xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
</xsl:template>
(将xmlns:msxsl=“urn:schemas-microsoft-com:xslt”
添加到xsl:stylesheet
)。或者如果要避免使用节点集
e
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:variable name="rtfLookupTable">
<lookuptable>
<val xpath="/first/xpath/expression" id="1" />
<val xpath="/second/xpath/expression" id="2" />
<!-- ... -->
</lookuptable>
</xsl:variable>
<xsl:variable name="lookupTable" select="msxsl:node-set($rtfLookupTable)" />
<xsl:key name="valByXpath" match="val" use="@xpath" />
<xsl:variable name="lookupTable" select="document('')//xsl:variable[name='rtfLookupTable']" />
<xsl:template match="text()">
<xsl:param name="curPath" />
<xsl:variable name="dot" select="." />
<xsl:variable name="slash" select="/" />
<xsl:for-each select="$lookupTable">
<xsl:variable name="valId" select="key('valByXpath', $curPath)/@id" />
<xsl:choose>
<xsl:when test="$valId">
<xsl:value-of select="$slash//Value[@id = $valId]" />
<!-- or however you extract the right Value -->
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$dot" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
/path/to/node1_1:/path/to/node2_2
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- copy everything as-is apart from exceptions below -->
<xsl:template match="@*|node()">
<xsl:copy><xsl:apply-templates select="@*|node()" /></xsl:copy>
</xsl:template>
<!-- delete the RealValuesRoot -->
<xsl:template match="RealValuesRoot" />
<xsl:template match="/path/to/node1">
<xsl:copy><xsl:value-of select="//Value[id='1']" /></xsl:copy>
</xsl:template>
<xsl:template match="/path/to/node2">
<xsl:copy><xsl:value-of select="//Value[id='2']" /></xsl:copy>
</xsl:template>
</xsl:stylesheet>