基于模式的合并帮助

时间:2009-07-09 13:24:59

标签: xml regex xpath

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

我的印象是,正则表达式反向引用可以帮助这种情况,但我已经走到了尽头。

2 个答案:

答案 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表达式,加上您无法识别行/记录概念的表达式。

此外,我不知道解决它的通用/普遍方式。我可能会用类似的方法来解决它:

  • 将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/$”占位符几乎可以显示在任何地方(而不仅仅是属性值),那么整个事情当然会变得复杂一些。一般方法可能仍然有效。