Справка по слиянию по шаблону - PullRequest
1 голос
/ 09 июля 2009

Существует ли общий подход к слиянию данных содержимого файла xml (шаблона) со встроенным выражением XPath в 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 ]

1 голос
/ 15 августа 2009

Это интересная проблема. Я предполагаю, что $/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 не будет работать.

0 голосов
/ 09 июля 2009

Тот факт, что ваш шаблон содержит $/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/$" могут появляться практически везде (вместо одних только значений атрибутов), все это, конечно, становится немного сложнее. Общий подход, вероятно, все еще будет работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...