Проблема слияния похожих XML-файлов с XSL - PullRequest
1 голос
/ 04 мая 2010

У меня есть два документа, которые мне нужно объединить, и это происходит так, что я не могу найти их в других примерах. А именно, это должно соответствовать не только атрибуту узла на одном уровне, но также и значению атрибута на уровне узла ниже этого, чтобы получить значение этого узла.

Я пытаюсь взять этот образец:

<?xml version="1.0" encoding="UTF-8" ?>
<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <marc:record>
    <marc:datafield tag="035" ind1=" " ind2=" ">
        <marc:subfield code="a">12345</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="041" ind1=" " ind2=" ">
        <marc:subfield code="a">eng</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="650" ind1=" " ind2="4">
        <marc:subfield code="a">Art</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="949" ind1=" " ind2=" ">
        <marc:subfield code="i">Review of conference proceedings</marc:subfield>
    </marc:datafield>
  </marc:record>
  <marc:record>
    <marc:datafield tag="035" ind1=" " ind2=" ">
        <marc:subfield code="a">54321</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="041" ind1=" " ind2=" ">
        <marc:subfield code="a">eng</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="650" ind1=" " ind2="4">
        <marc:subfield code="a">Byzantine</marc:subfield>
    </marc:datafield>
  </marc:record>
</marc:collection>

И когда значение «поля данных» «035», «подполе» «а» совпадает, например, "12345"

<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim"
xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
  <marc:record>
    <marc:datafield ind2=" " ind1=" " tag="035">
        <marc:subfield code="a">12345</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2="4" ind1=" " tag="650">
        <marc:subfield code="a">General works</marc:subfield>
        <marc:subfield code="x">Historians and critics</marc:subfield>
        <marc:subfield code="x">Smith, John, 1834-1917</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2="4" ind1=" " tag="650">
        <marc:subfield code="a">Généralités</marc:subfield>
        <marc:subfield code="x">Historiens et critiques d'art</marc:subfield>
        <marc:subfield code="x">Dietrichson, Lorentz, 1834-1917</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2=" " ind1=" " tag="654">
        <marc:subfield code="a">General works</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2=" " ind1=" " tag="654">
        <marc:subfield code="a">Généralités</marc:subfield>
        <marc:subfield code="b">Historiens et critiques d'art</marc:subfield>
        <marc:subfield code="b">Smith, John, 1834-1917</marc:subfield>
    </marc:datafield>
  </marc:record>      
  <marc:record>
    <marc:datafield ind2=" " ind1=" " tag="035">
        <marc:subfield code="a">54321</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2="4" ind1=" " tag="650">
        <marc:subfield code="a">General works</marc:subfield>
        <marc:subfield code="x">Historians and critics</marc:subfield>
        <marc:subfield code="x">Lange, Julius Henrik, 1838-1896</marc:subfield>
    </marc:datafield>
  </marc:record>
</marc:collection>

Результат должен быть:

<?xml version="1.0" encoding="UTF-8" ?>
<marc:collection xmlns:marc="http://www.loc.gov/MARC21/slim"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <marc:record>
    <marc:datafield tag="035" ind1=" " ind2=" ">
        <marc:subfield code="a">12345</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="041" ind1=" " ind2=" ">
        <marc:subfield code="a">eng</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="650" ind1=" " ind2="4">
        <marc:subfield code="a">Art</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2="4" ind1=" " tag="650">
        <marc:subfield code="a">General works</marc:subfield>
        <marc:subfield code="x">Historians and critics</marc:subfield>
        <marc:subfield code="x">Smith, John, 1834-1917</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2="4" ind1=" " tag="650">
        <marc:subfield code="a">Généralités</marc:subfield>
        <marc:subfield code="x">Historiens et critiques d'art</marc:subfield>
        <marc:subfield code="x">Dietrichson, Lorentz, 1834-1917</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2=" " ind1=" " tag="654">
        <marc:subfield code="a">General works</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2=" " ind1=" " tag="654">
        <marc:subfield code="a">Généralités</marc:subfield>
        <marc:subfield code="b">Historiens et critiques d'art</marc:subfield>
        <marc:subfield code="b">Smith, John, 1834-1917</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="949" ind1=" " ind2=" ">
        <marc:subfield code="i">Review of conference proceedings</marc:subfield>
    </marc:datafield>
  </marc:record>
  <marc:record>
    <marc:datafield tag="035" ind1=" " ind2=" ">
        <marc:subfield code="a">54321</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="041" ind1=" " ind2=" ">
        <marc:subfield code="a">eng</marc:subfield>
    </marc:datafield>
    <marc:datafield tag="650" ind1=" " ind2="4">
        <marc:subfield code="a">Byzantine</marc:subfield>
    </marc:datafield>
    <marc:datafield ind2="4" ind1=" " tag="650">
        <marc:subfield code="a">General works</marc:subfield>
        <marc:subfield code="x">Historians and critics</marc:subfield>
        <marc:subfield code="x">Lange, Julius Henrik, 1838-1896</marc:subfield>
    </marc:datafield>
  </marc:record>
</marc:collection>

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

Спасибо!

Ответы [ 2 ]

0 голосов
/ 07 мая 2010

Следующее решение использует ключи для эффективного поиска в объединенном документе. Предполагается, что должны быть скопированы все элементы datafield, за исключением соответствующего datafield, и что для каждого record будет самое большее один соответствующий datafield. URL-адрес документа, который должен быть объединен, передается в качестве параметра.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:marc="http://www.loc.gov/MARC21/slim">
    <xsl:output method="xml" indent="yes"/>
    <xsl:param name="mergeFile"/>
    <xsl:variable name="mergeDoc" select="document($mergeFile)"/>

    <xsl:key name="datafield" match="marc:datafield" 
        use="concat(@tag, '|', marc:subfield[@code='a'])"/>

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

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

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

    <xsl:template match="marc:datafield" mode="merge">
        <xsl:variable name="datafieldKey" 
                      select="concat(@tag, '|', marc:subfield[@code='a'])"/>
        <!-- Make the other document the context node with for-each, so that
             key lookups will consult that document instead of the source 
             document. -->
        <xsl:for-each select="$mergeDoc">
            <xsl:for-each select="key('datafield', $datafieldKey)">
                <xsl:copy-of select="preceding-sibling::*"/>
                <xsl:copy-of select="following-sibling::*"/>
            </xsl:for-each>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>
0 голосов
/ 06 мая 2010

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

<?xml version="1.0" encoding="UTF-8"?>

<xsl:variable name="doc2" select="document('FourBabyMarcs.xml')"/>

<xsl:template match="/">
    <marc:collection>
        <xsl:for-each select="marc:collection/marc:record">
            <marc:record>

                <xsl:for-each select="marc:leader">
                    <xsl:copy-of select="."/>
                </xsl:for-each>

                <xsl:for-each select="marc:controlfield">
                    <xsl:copy-of select="."/>
                </xsl:for-each>

                <xsl:for-each select="marc:datafield">
                    <xsl:copy-of select="."/>
                </xsl:for-each>

                <xsl:variable name="ID">
                    <xsl:value-of select="marc:datafield[@tag='035']/marc:subfield[@code='a']"/>
                </xsl:variable>

                <xsl:for-each select="$doc2/*/marc:record">
                        <xsl:if test="marc:datafield[@tag='035']/marc:subfield[@code='a']=$ID">
                            <xsl:for-each select="marc:datafield">
                                <xsl:if test="@tag='650'">
                                    <xsl:copy-of select="."/>
                                </xsl:if>
                                <xsl:if test="@tag='654'">
                                    <xsl:copy-of select="."/>
                                </xsl:if>
                            </xsl:for-each>
                        </xsl:if>
                </xsl:for-each>
            </marc:record>
        </xsl:for-each>
    </marc:collection>
</xsl:template>

...