Главная » XSLT » Объединение документов с различными схемами

0

Задача

Имеется несколько документов с разной структурой, а требуется объединить их в один.

Решение

Процедура объединения структурно различных документов зависит от конк­ретного приложения. Поэтому мы не можем предложить одно общее решение. Однако мы попробуем прикинуть, в каких случаях обычно возникает такая необ­ходимость, и приведем решение для каждого случая.

Включить документ в качестве подраздела родительского документа

Включение документа в качестве подраздела в другой документ – это про­стейшая интерпретация объединения такого вида. Идея решения состоит в том, чтобы воспользоваться командой xsl:copy-of для копирования всего докумен­та или его части в подходящее место другого документа. В следующем примере мы вставляем два документа в документ-контейнер, предполагая, что имена эле­ментов в контейнере определяют, какие файлы нужно вставлять.

<MyNoteBook> <friends> </friends> <coworkers> </coworkers> <projects>

<project>Заменить mapML на XSLT с помощью Xalan C++</project> <project>Выяснить, в чем смысл жизни.</project>

<project>Pазобраться, куда деваются носки из сушилки.</project> </projects>

</MyNoteBook>

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

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:strip-space elements="*"/>

<xsl:template match="friends | coworkers"> <xsl:copy>

<xsl:variable name="file" select="concat(local-name(),’.xml’)"/> <xsl:copy-of select="document($file)/*/*"/> </xsl:copy> </xsl:template>

</xsl:stylesheet>

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

<MyNoteBook> <friends>

<person firstname="Sal" lastname="Mangano" age="38" height="5.75"/> <person firstname="Mike" lastname="Palmieri" age="28" height="5.10"/> <person firstname="Vito" lastname="Palmieri" age="38" height="6.0"/> <person firstname="Vinny" lastname="Mari" age="37" height="5.8"/> </friends> <coworkers>

<person firstname="Sal" lastname="Mangano" age="38" height="5.75"/> <person firstname="Al" lastname="Zehtooney" age="33" height="5.3"/> <person firstname="Brad" lastname="York" age="38" height="6.0"/> <person firstname="Charles" lastname="Xavier" age="32" height="5.8"/> </coworkers> <projects>

<project>Заменить mapML на XSLT с помощью Xalan C++</project>

<project>BbrncHMTb, в чем смысл XM3HM.</project>

<project>Pa3o6paTbcn, куда деваются носки из cymnnKH.</project> </projects> </MyNoteBook>

Интересная вариация на эту тему – документ, в котором декларативно требу­ется включить другой документ в определенное место. Консорциум W3C опреде­ляет стандартный способ решения этой задачи – XInclude (http://www.w3.org/TR/ xinclude/). На языке XSLT можно реализовать универсальный процессор XInclude, обобщив таблицу copy.xslt:

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

<xsl:import href="copy.xslt"/>

<xsl:output method="xml" indent="yes"/> <xsl:strip-space elements="*"/>

<xsl:template match="xi:include" xmlns:xi="http://www.w3.org/2001/ XInclude">

<xsl:for-each select="document(@href)">

<xsl:apply-templates/> </xsl:for-each> </xsl:template>

</xsl:stylesheet>

Команда xsl:for-each всего лишь переключает контекст на включаемый документ. Следующая за ней команда xsl:apply-templates продолжает ко­пировать содержимое включаемого документа.

Сплетение двух документов

Это вариант простого включения, когда объединяются дочерние элементы однотипных родителей. Рассмотрим двух биологов, которые порознь собирали информацию о различных животных. Перед тем как построить общую базу дан­ных, они должны привести свои файлы к единой структуре. У биолога 1 есть такой файл:

<animals> <mammals>

<animal common="chimpanzee" species="Pan troglodytes" order="Primates"/> <animal common="human" species="Homo Sapien" family="Primates"/> </mammals> <reptiles>

<animal common="boa constrictor" species="Boa constrictor"order="Squamata"/> <animal common="gecko" species="Gekko gecko" order="Squamata"/> </reptiles> <birds>

<animal common="sea gull" species="Larus occidentalis"

order="Charadriiformes"/> <animal common="Black-Backed Woodpecker" species="Picoides arcticus" order="Piciformes"/> </birds> </animals>

А у биолога 2 такой:

<animals> <mammals>

<animal common="hippo" species="Hippopotamus amphibius" family=" Hippopotamidae"/>

<animal common="arabian camel" species="Camelus dromedarius" family="Camelidae"/> </mammals> <insects>

<animal common="Lady Bug" species="Adalia bipunctata" family="Coccinellidae"/>

<animal common="Dung Bettle" species=" Onthophagus australis" family="Scarabaeidae"/> </insects> <amphibians>

<animal common="Green Sea Turtle" species="Chelonia mydas" family="Cheloniidae"/>

<animal common="Green Tree Frog" species=" Hyla cinerea" family="Hylidae "/> </amphibians> </animals>

У этих файлов схемы похожи, но не идентичны. И там, и там есть класс млеко­питающих (mammals), но его подуровни организованы по-разному. На уровне animal первый биолог включил информацию об отряде (order), а второй – о семей­стве (family). Следующая таблица стилей сплетает документы на уровне класса (второй уровень в структуре документа):

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

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:strip-space elements="*"/>

<xsl:param name="doc2file"/>

<xsl:variable name="doc2" select="document($doc2file)"/> <xsl:variable name="thisDocsClasses" select="/*/*"/>

<xsl:copy>

<!— Объединить общие разделы в документах doc и doc2. Включить также уникальные разделы документа doc. –> <xsl:for-each select="*"> <xsl:copy>

<xsl:copy-of select="*"/>

<xsl:copy-of select="$doc2/*/*[name() = name(current())]/*"/> </xsl:copy> </xsl:for-each>

<!– Включить уникальные разделы doc2 –> <xsl:for-each select="$doc2/*/*"> <xsl:if test="not($thisDocsClasses[name() = name(current())])">

<xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </xsl:copy> </xsl:template>

</xsl:stylesheet>

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

<animals> <mammals>

<animal common="chimpanzee" species="Pan troglodytes" order="Primates"/> <animal common="human" species="Homo Sapien" order="Primates"/> <animal common="hippo" species="Hippopotamus amphibius" family=" Hippopotamidae"/>

<animal common="arabian camel" species="Camelus dromedarius" family="Camelidae"/> </mammals> <reptiles>

<animal common="boa constrictor" species="Boa constrictor" order="Squamata"/>

<animal common="gecko" species="Gekko gecko" order="Squamata"/> </reptiles> <birds>

<animal common="sea gull" species="Larus occidentalis" order="Charadriiformes"/>

<animal common="Black-Backed Woodpecker" species="Picoides arcticus" order="Piciformes"/> </birds>

<insects>

<animal common="Lady Bug" species="Adalia bipunctata" family="Coccinellidae"/>

<animal common="Dung Bettle" species=" Onthophagus australis" family="Scarabaeidae"/> </insects> <amphibians>

<animal common="Green Sea Turtle" species="Chelonia mydas" family="Cheloniidae"/>

<animal common="Green Tree Frog" species=" Hyla cinerea" family="Hylidae "/> </amphibians> </animals>

Соединение элементов из двух документов для получения новых элементов

Менее тривиальный случай – это наложение одного документа на другой с возможным порождением дочерних элементов в зависимости от критериев со­поставления. Рассмотрим, к примеру, следующий способ объединения докумен­тов, содержащих различную информацию о людях:

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

<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:param name="doc2file"/>

<xsl:variable name="doc2" select="document($doc2file)"/>

<xsl:template match="person"> <xsl:copy>

<xsl:for-each select="@*">

<xsl:element name="{local-name()}">

<xsl:value-of select="."/> </xsl:element> </xsl:for-each>

<xsl:variable name="matching-person"

select="$doc2/*/person[@name=concat(current()/@firstname,’ ‘,

current()/@lastname)]"/>

<xsl:element name="smoker">

<xsl:value-of select="$matching-person/@smoker"/> </xsl:element> <xsl:element name="sex">

<xsl:value-of select="$matching-person/@sex"/> </xsl:element> </xsl:copy> </xsl:template>

</xsl:stylesheet>

Эта таблица стилей решает две задачи. Во-первых, информация, представ­ленная во входных документах в виде атрибутов, перекодируется в элементы, а, во-вторых, копируется информация из $doc2, отсутствующая в первом до­кументе.

Обсуждение

Объединение XML-документов с разными схемами нельзя определить так же однозначно, как объединение структурно одинаковых документов. В этой главе были рассмотрены три интерпретации объединения, но возможны и более слож­ные варианты. Так, для объединения нескольких документов может потребовать­ся некая комбинация включения, сплетения и соединения. Поэтому, трудно напи­сать на XSLT единую обобщенную утилиту, которая покрывала бы все мыслимые потребности. Тем не менее, приведенные примеры показывают, с чего начинать, когда нужно реализовать какую-нибудь хитроумную схему объединения.

См. также

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

Мангано Сэл  XSLT. Сборник рецептов. – М.: ДМК Пресс, СПБ.: БХВ-Петербург, 2008. – 864 с.: ил.

По теме:

  • Комментарии