Слить информацию из двух файлов xml в один, используя xslt

Файл a.xml:

<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="pivot.cs">
   <DATA RECORDS="2">
      <RECORD ID="1">
         <INTERNALID>5510</INTERNALID>
         <SOMED>1</SOMED>
         <PEMED>1</PEMED>
         <CODAL>PLACEHOLD</CODAL>
      </RECORD>
      <RECORD ID="2">
         <INTERNALID>5511</INTERNALID>
         <SOMED>1</SOMED>
         <PEMED>1</PEMED>
         <CODAL>PLACEHOLD</CODAL>
      </RECORD>
      <INTERNALID>5537</INTERNALID>
      <SOMED>1</SOMED>
      <PEMED>1</PEMED>
      <CODAL>PLACEHOLD</CODAL>
   </DATA>
</TABLE>

файл b.xml:

    <?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="ALT.CS">
   <DATA RECORDS="20">
      <RECORD ID="53">
         <RECNO>5510</RECNO>
         <TOBEEXTRACTED>TIM</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="53">
         <RECNO>5510</RECNO>
         <TOBEEXTRACTED>KLM</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="54">
         <RECNO>5510</RECNO>
         <TOBEEXTRACTED>KAB</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="55">
         <RECNO>5511</RECNO>
         <TOBEEXTRACTED>BUS WEE</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="59">
         <RECNO>5512</RECNO>
      </RECORD>
      <RECORD ID="60">
         <RECNO>5513</RECNO>
         </RECORD>
         <RECORD ID="5511">
            <RECNO>5598</RECNO>
            <TOBEEXTRACTED>FBV</TOBEEXTRACTED>
         </RECORD>
      </RECORD>
   </DATA>
</TABLE>

а выходной файл должен быть файлом a.xml, но с текстом элемента TOBEEXTRACTED, добавленным в [], если совпадают один или два раза:

<?xml version="1.0" encoding="UTF-8"?>
<TABLE NAME="pivot.cs">
   <DATA RECORDS="2">
      <RECORD ID="1">
         <INTERNALID>5510</INTERNALID>
         <SOMED>1</SOMED>
         <PEMED>1</PEMED>
         <CODAL>PLACEHOLD</CODAL>
      </RECORD>
      <RECORD ID="2">
         <INTERNALID>5511</INTERNALID>
         <SOMED>1</SOMED>
         <PEMED>1</PEMED>
         <CODAL>PLACEHOLD [BUS WEE]</CODAL>
      </RECORD>
      <INTERNALID>5537</INTERNALID>
      <SOMED>1</SOMED>
      <PEMED>1</PEMED>
      <CODAL>PLACEHOLD</CODAL>
   </DATA>
</TABLE>

Кроме того, было бы очень полезно, если бы у нас был текстовый файл в качестве вывода, который имел бы следующую информацию: из файла a.xml,

INTERNALID: 5511 (and all the rest in a normal xml file) was matched.
INTERNALID: 5510 was matched more than two times, so no join took place.
INTERNALID: 5537 did not match
RECNO 5512 did not have a TOBEEXTRACTED element.

person Community    schedule 12.05.2017    source источник
comment
Можете ли вы по крайней мере отредактировать входные и желаемые образцы выходных данных, чтобы показать примеры интересующих вас случаев (совпадающие элементы, не совпадающие, совпадающие более двух раз, совпадающие без элемента TOBEEXTRACTED?   -  person Martin Honnen    schedule 12.05.2017
comment
Также подумайте о том, чтобы продемонстрировать свою попытку решить эту проблему с помощью XSLT 2.0, вы можете достаточно легко решить перекрестную ссылку с помощью ключа <xsl:key name="ref" match="DATA/RECORD[TOBEEXTRACTED]" use="RECNO"/>, а затем сопоставить <xsl:template match="DATA/RECORD[key('ref', INTERNALID, doc('fileb.xml')]/CODAL"><xsl:copy><xsl:value-of select="concat(., ' [', key('ref', ../INTERNALID, doc('fileb.xml'))/TOBEEXTRACTED, ']')"/></xsl:copy></xsl:template>   -  person Martin Honnen    schedule 12.05.2017
comment
Надеюсь, теперь это более ясно, спасибо   -  person    schedule 12.05.2017


Ответы (2)


Если вы используете ключ, как предложено в комментарии, вы можете ссылаться на элементы и сопоставлять их следующим образом:

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

    <xsl:param name="doc2">
        <TABLE NAME="ALT.CS">
   <DATA RECORDS="20">
      <RECORD ID="53">
         <RECNO>5510</RECNO>
         <TOBEEXTRACTED>TIM</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="53">
         <RECNO>5510</RECNO>
         <TOBEEXTRACTED>KLM</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="54">
         <RECNO>5510</RECNO>
         <TOBEEXTRACTED>KAB</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="55">
         <RECNO>5511</RECNO>
         <TOBEEXTRACTED>BUS WEE</TOBEEXTRACTED>
      </RECORD>
      <RECORD ID="59">
         <RECNO>5512</RECNO>
      </RECORD>
      <RECORD ID="60">
         <RECNO>5513</RECNO>
         </RECORD>
         <RECORD ID="5511">
            <RECNO>5598</RECNO>
            <TOBEEXTRACTED>FBV</TOBEEXTRACTED>
         </RECORD>

   </DATA>
</TABLE>
    </xsl:param>

    <xsl:key name="ref" match="DATA/RECORD[TOBEEXTRACTED]" use="RECNO"/>

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

    <xsl:template match="DATA/RECORD[key('ref', INTERNALID, $doc2)]/CODAL">
        <xsl:copy>
            <xsl:apply-templates select="node(), key('ref', ../INTERNALID, $doc2)/TOBEEXTRACTED"/>  
        </xsl:copy>
    </xsl:template>

    <xsl:template match="DATA/RECORD[not(key('ref', INTERNALID, $doc2))]"/>

    <xsl:template match="TOBEEXTRACTED">
        <xsl:value-of select="concat(' [', ., ']')"/>
    </xsl:template>

</xsl:transform>

Это дает результат, который вы разместили на http://xsltransform.net/a9Giwy. Здесь я использовал xsl:param name="doc2" со встроенным содержимым, но вы, конечно, можете использовать вместо него <xsl:param name="doc2" select="doc('fileb.xml')"/>.

Как и при редактировании, вопрос был дополнительно помечен как xslt -3.0 Я также пытался реализовать это с помощью xsl:merge инструкции этой версии:

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

    <xsl:param name="doc2-uri" as="xs:string" select="'test201705120102.xml'"/>

    <xsl:mode on-no-match="shallow-copy"/>

    <xsl:output indent="yes"/>

    <xsl:template match="TABLE/DATA">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:merge>
                <xsl:merge-source name="internal" select="RECORD" >
                    <xsl:merge-key select="INTERNALID"/>
                </xsl:merge-source>
                <xsl:merge-source name="recno" select="doc($doc2-uri)//RECORD">
                    <xsl:merge-key select="RECNO"/>
                </xsl:merge-source>
                <xsl:merge-action>
                    <xsl:if test="current-merge-group('internal') and current-merge-group('recno')">
                        <xsl:copy>
                            <xsl:copy-of select="@*, * except CODAL"/>
                            <CODAL>
                                <xsl:value-of select="CODAL, current-merge-group('recno')/TOBEEXTRACTED/('[' || . || ']')"/>
                            </CODAL>
                        </xsl:copy>
                    </xsl:if>
                </xsl:merge-action>
            </xsl:merge>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>
person Martin Honnen    schedule 12.05.2017
comment
На самом деле я хотел бы, чтобы все изменения были перезаписаны в исходный XML, по ошибке в выходном файле я включил только измененные записи и, наконец, как я могу изменить приведенные выше примеры, чтобы получить отчет о том, что было изменено, а что не было? - person ; 13.05.2017
comment
Я уверен, что XSLT - это язык программирования, и его можно использовать для сообщения деталей, но ваш вопрос даже не прояснил, нужно ли и где вы хотите эту информацию в показанном вами образце результатов, и хотите ли вы, чтобы она была включена во второй result, поэтому я сосредоточился на ответе на ту часть, которая, казалось, содержала четкое описание требований. Если вы отредактируете свой вопрос и объясните и покажете, где и как вы хотите получить информацию, я уверен, что мы сможем помочь с кодом. - person Martin Honnen; 13.05.2017
comment
переписали мой вопрос с надеждой, что теперь он стал более ясным - person ; 13.05.2017

Такое слияние часто можно выполнить с помощью xsl: for-each-group:

<xsl:for-each-group select="$doc1//REC, $doc2//REC" group-by="RECNO">
  ...
</xsl:for-each-group>

в теле current-group () хранятся записи из обоих файлов с требуемым ключом. Вы можете разделить их, например,

<xsl:variable name="doc1rec" select="current-group()[(/) is $doc1]"/>
<xsl:variable name="doc2rec" select="current-group()[(/) is $doc2]"/>

а затем оставшаяся обработка должна быть простой, если вы понимаете логику (а я не понимаю).

person Michael Kay    schedule 12.05.2017