是否有通用方法将带有嵌入式XPath表达式的xml文件内容(模板)数据合并到XmlDocument?
作为一个例子,(请注意这只是一个简单的例子,我正在寻找通用的方法)
文件:
<root xmlns:dt="urn:schemas-microsoft-com:datatypes">
<session email='' alias=''>
<state>
<action>
<attribute in_var="" out_var="" entity_name="entity" query_name="query1"/>
<attribute dtype="string" in_var="" name="entity_id" value="$/data/row/entity_id$"/>
</action>
</state>
</session>
的XmlDocument:
<data>
<row>
<entity_id>1</entity_id>
<entity_name>Entity 1</entity_name>
</row>
<row>
<entity_id>2</entity_id>
<entity_name>Entity 2</entity_name>
</row>
</data>
合并后:
<root xmlns:dt="urn:schemas-microsoft-com:datatypes">
<session email='' alias=''>
<state>
<action>
<attribute in_var="" out_var="" entity_name="entity" query_name="query1"/>
<attribute dtype="string" in_var="" name="entity_id" value="1"/>
</action>
</state>
</session>
<root xmlns:dt="urn:schemas-microsoft-com:datatypes">
<session email='' alias=''>
<state>
<action>
<attribute in_var="" out_var="" entity_name="entity" query_name="query1"/>
<attribute dtype="string" in_var="" name="entity_id" value="2"/>
</action>
</state>
</session>
我的印象是,正则表达式反向引用可以帮助这种情况,但我已经走到了尽头。
答案 0 :(得分:1)
这是一个有趣的问题。我假设$/some/path/$
将始终替换为XPath查询返回的元素的值?我认为“文件”必须作为字符串处理。是的,它是一个XML,但如果这种模式成立,那么这种方式就会简单得多。那只是一个宏观替代。
在这种情况下,一个解决方案是(Scala脚本):
import scala.xml.{Node, NodeSeq}
val pattern = """\$([\w/]*)\$""".r
def patterns(s: String) = (pattern findAllIn s matchData) map (_ group 1) toList
def pathComponents(path: String) = (path split """\b(?!\w)""" toList) map (_ split "\\b" toList)
def lookUp(xml: Node, path: List[List[String]]) = {
path.foldLeft(xml : NodeSeq) { (nodes, pathComponent) =>
pathComponent match {
case List("/", component) => nodes \ component
case List("//", component) => nodes \\ component
case _ => throw new IllegalArgumentException
}
} map (_ text)
}
def pathAndValues(s: String, xml: Node) = {
patterns(s) map (path => (path -> lookUp(xml, pathComponents(path))))
}
def merge(s: String, xml: Node) = {
pathAndValues(s, xml).foldLeft(List(s)) { (files, tuple) =>
val (path, values) = tuple
for (file <- files;
value <- values)
yield file replace ("$"+path+"$", value)
}
}
然后将XmlDocument读入xml,并将文件合并到String中。当然,这可以说明文件不是太大而无法以这种方式处理。在Scala中,可以这样做:
merge(scala.io.Source.fromFile(filename).getLines.mkString,
scala.xml.XML.loadFile(XmlDocumentFilename))
这将返回一个列表,其中包含每次替换的每个排列。
如果这些文件太大而无法保留在内存中,则需要为要替换的值生成每个可能的排列,这样您只需要一次传递来替换每个排列的所有路径。
如果XPath是真正的XPath,而不仅限于“/”和“//”,则此解决方案不会按原样执行。它必须转换为使用真正的XPath库。另请注意,“/”会查找孩子,因此如果<data>
是根,则/data
将无效。
答案 1 :(得分:0)
您的模板包含$/xpath/expression$
字符串的事实几乎排除了单独在XSLT中解决此问题的可能性 - 无法动态评估XPath表达式,加上您无法识别行/记录概念的表达式。
此外,我不知道解决它的通用/普遍方式。我可能会用类似的方法来解决它:
//@*[starts-with(., '$') and ends-with(., '$')]
说,您的模板包含以下模式:
然后我将从为每个表达式(伪JS代码)创建结果集开始:
var placeholderData = {
"$/data/row/entity_id$": ["1", "2"],
"$/data/row/entity_name$": ["Entity 1", "Entity 2"]
};
然后,我将在<row>
s(再次伪代码)上循环:
var rows = dataXml.selectNodes("/data/row");
var placeholderXpath = "//@*[starts-with(., '$') and ends-with(., '$')]";
for (var i = 0; i < rows.length; i++)
{
var currentTemplate = templateXml.copy();
var attributeNode = null;
foreach (attributeNode in currentTemplate.selectNodes(placeholderXpath))
{
var expression = attributeNode.text;
if (placeholderData[expression].length > i)
attributeNode.text = placeholderData[expression][i];
else
attributeNode.text = "";
}
currentTemplate.saveAs("output_" + i + ".xml");
}
如果“$/xpath/expression/$
”占位符几乎可以显示在任何地方(而不仅仅是属性值),那么整个事情当然会变得复杂一些。一般方法可能仍然有效。