2009-07-09 40 views
1

是否有一种通用方法将具有嵌入式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

你在说什么听起来很像XSLT。你有没有调查过它作为一个选项? http://www.w3schools.com/xsl/ – Welbog 2009-07-09 13:29:32

+2

Backrefrences与否 - 我明确建议不要试用Regex作为解决此问题的方法。 – Tomalak 2009-07-09 13:39:20

回答

0

事实上,您的模板包含$/xpath/expression$字符串几乎排除了仅在XSLT中解决此问题的可能性 - XPath表达式不能动态计算,加上表达式不能识别行/记录的概念。

另外我不知道通用/广泛的解决方法。我可能会用类似上述的方法解决这个问题:

  • 读取XML模板文件为DOM,XML数据文件到另一个DOM
  • 找XPath表达式的占位符。例如,如果它们带有属性:
    //@*[starts-with(., '$') and ends-with(., '$')]
  • 拉出所有XPath表达式字符串并将它们应用于数据文件,将结果存储在临时数据结构中。

说,你的模板包含在这些模式:

  • “$ /数据/行/ ENTITY_ID $”
  • “$ /数据/行/ ENTITY_NAME $”

然后我将首先为每个表达式(伪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/$”占位符可以显示几乎任何地方(而不是单独的属性值的),整个事情变得当然有点复杂。一般的做法可能仍然有效。

1

这是一个有趣的问题。我假设$/some/path/$将始终由XPath查询返回的元素的值替换?我认为“文件”必须作为一个字符串处理。是的,这是一个XML,但如果这种模式成立,那么这种方法就简单多了。那只是一个宏观替代。

在这种情况下,一个解决方案是(斯卡拉脚本):

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,文件合并成字符串。当然,这个文件不会太大,不能以这种方式处理。在斯卡拉,它可以这样做:

merge(scala.io.Source.fromFile(filename).getLines.mkString, 
     scala.xml.XML.loadFile(XmlDocumentFilename)) 

这将返回一个列表,每个替换可能的每个排列。

如果这些文件太大而无法保存在内存中,则需要为要替换的值生成每个可能的置换,因此您只需要一遍就可以替换每个置换的所有路径。

如果XPath是真XPath,而不仅限于“/”和“//”,则此解决方案不会按原样进行。它必须转换为使用真正的XPath库。此外,请注意“/”查找孩子,因此如果<data>是根,/data将不起作用。