XSLT: замена узла эквивалентным узлом другого документа

Я хочу заменить некоторые узлы XML-файла эквивалентными узлами другого XML-файла. Поскольку это было бы недостаточно сложно, я хочу, чтобы идентификатор, используемый для сравнения, был значением некоторого дочернего элемента.

«Старый» XML выглядит так:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
    <Documents>
        <Document id="001">
            <Tags>
                <Tag id="document_id">someIDfilename.pdf</Tag>
                <Tag id="document_type">Type A</Tag>
                <Tag id="document_text">A very important document of course.</Tag>
            <Tags>
        </Document>
        <Document id="018">
            <Tags>
                <Tag id="document_id">someOtherIDfilename.pdf</Tag>
                <Tag id="document_type">Type B</Tag>
                <Tag id="document_text">Another very important document.</Tag>
            <Tags>
        </Document>
    </Documents>
</Root>

Второй документ должен быть заменен эквивалентом следующего XML, при этом идентификатор, который я должен использовать, является значением document_id (поскольку «id» узла документа иногда перезаписывается или изменяется):

<?xml version="1.0" encoding="UTF-8"?>
<Root>
    <Documents>
        <Document id="014">
            <Tags>
                <Tag id="document_id">someOtherIDfilename.pdf</Tag>
                <Tag id="document_type">Type B</Tag>
                <Tag id="document_text">The oh so important new document text.</Tag>
            <Tags>
        </Document>
    </Documents>
</Root>

Ожидается, что результат будет выглядеть так:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
    <Documents>
        <Document id="001">
            <Tags>
                <Tag id="document_id">someIDfilename.pdf</Tag>
                <Tag id="document_type">Type A</Tag>
                <Tag id="document_text">A very important document of course.</Tag>
            <Tags>
        </Document>
        <Document id="018">
            <Tags>
                <Tag id="document_id">someOtherIDfilename.pdf</Tag>
                <Tag id="document_type">Type B</Tag>
                <Tag id="document_text">The oh so important new document text.</Tag>
            <Tags>
        </Document>
    </Documents>
</Root>

Q1: возможно ли это с помощью XSLT? Или я должен использовать Java/DOM?

Q2: Если Q1==да: кто-нибудь может решить это здесь?

Лучший! Филипп


person user3465206    schedule 26.03.2014    source источник
comment
Вы используете процессор XSLT 1.0 или 2.0? И почему атрибут id в желаемом результате имеет значение 018, а не 014 из второго документа, из которого вы сказали, что хотите взять замену?   -  person Martin Honnen    schedule 26.03.2014
comment
В настоящее время мне разрешено использовать только процессор XSLT 1.0. Старый атрибут id был бы предпочтительнее по причинам дальнейшей обработки.   -  person user3465206    schedule 27.03.2014


Ответы (1)


Использование процессора XSLT 2.0, такого как Saxon 9:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">

<xsl:param name="doc2-url" select="'test2014032603.xml'"/>
<xsl:variable name="doc2" select="doc($doc2-url)"/>

<xsl:key name="id" match="Document" use="Tags/Tag[@id = 'document_id']"/>

<xsl:template match="@* | node()">
  <xsl:copy>
    <xsl:apply-templates select="@* , node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Document[key('id', Tags/Tag[@id = 'document_id'], $doc2)]">
  <xsl:copy>
    <xsl:copy-of select="@id, key('id', Tags/Tag[@id = 'document_id'], $doc2)/node()"/>
  </xsl:copy>
</xsl:template>

</xsl:stylesheet>

С XSLT 1.0 это также возможно, но для переключения контекстов между документами для использования ключа код оказывается запутанным:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

<xsl:param name="doc2-url" select="'test2014032603.xml'"/>
<xsl:variable name="doc2" select="document($doc2-url)"/>

<xsl:key name="id" match="Document" use="Tags/Tag[@id = 'document_id']"/>

<xsl:template match="@* | node()" name="identity">
  <xsl:copy>
    <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Document[Tags/Tag[@id = 'document_id']]">
  <xsl:variable name="this" select="."/>
  <xsl:for-each select="$doc2">
    <xsl:choose>
      <xsl:when test="key('id', $this/Tags/Tag[@id = 'document_id'])">
        <xsl:for-each select="key('id', $this/Tags/Tag[@id = 'document_id'])">
          <xsl:copy>
            <xsl:copy-of select="$this/@id"/>
            <xsl:copy-of select="node()"/>            
          </xsl:copy>
        </xsl:for-each>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select="$this"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:for-each>
</xsl:template>

</xsl:stylesheet>
person Martin Honnen    schedule 26.03.2014