XML - XSLT - Использование двух XML-файлов - Дополнения к XML-файлу, с которыми можно ознакомиться в другом XML-файле - PullRequest
0 голосов
/ 06 июля 2018

У меня есть следующая проблема, которая для меня немного сложна,

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

У меня есть следующий XML-файл, который я хочу изменить (в основном, просто добавив в него):

<?xml version="1.0" encoding="UTF-8"?>
<report xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.23" id="1">
    <text-prop name="displayName">PersonTemplate</text-prop>
    <setup>
        <simple-master-page name="MasterPage" id="2">
            <footer>
                <text id="3">
                    <prop name="contentType">html</prop>
                    <text-prop name="content"><![CDATA[<value-of>new Date()</value-of>]]></text-prop>
                </text>
            </footer>
        </simple-master-page>
    </setup>
    <body>
        <table id="4">  
            <column id="17"/>
            <column id="18"/>
            <column id="19"/>
            <header>
                <row id="5">
                    <cell id="6">
                        <label id="20">
                            <text-prop name="text">NameTitle</text-prop>
                        </label>
                    </cell>
                    <cell id="7">
                        <label id="21">
                            <text-prop name="text">CityTitle</text-prop>
                        </label>
                    </cell>
                    <cell id="8">
                        <label id="22">
                            <text-prop name="text">AgeTitle</text-prop>
                        </label>
                    </cell>
                </row>
            </header>
            <detail>
                <row id="9">
                    <cell id="10"/>
                    <cell id="11"/>
                    <cell id="12"/>
                </row>
            </detail>
        </table>
    </body>
</report>

И я хочу внести в него изменения / дополнения, обратившись к другому XML-файлу, который дает мне данные, которые я хочу поместить в первый XML-файл:

<?xml version="1.0" encoding="utf-8"?>
<model>
    <layouts>
        <layout ID="001" name="PersonTemplate" format="Table" nFields="3" >
            <fields>
                <field name="NameTitle"/>
                <field name="CityTitle"/>
                <field name="AgeTitle"/>
            </fields>
        </layout>
        <layout ID="002" name="SchoolTemplate" format="Table" nFields="3" >
            <fields>
                <field name="NameTitle"/>
                <field name="LocationTitle"/>
                <field name="MaxCapacityTitle"/>
            </fields>
        </layout>
    </layouts>
    <reports>
        <report layoutID="001">
            <params>
                <sources>
                    <source name="source1" dbURL="sampledb1.com" user="user1" password="dXNlcjE=" driver="dbDriver"/>
                    <source name="source2" dbURL="sampledb2.com" user="user2" password="dXNlcjI=" driver="dbDriver"/>
                </sources>
                <set name="set1" source="source1" querie="select Name, City, Age from PeopleTable" >
                    <qFields>
                        <qField name="Name" type="string"/>
                        <qField name="City" type="string"/>
                        <qField name="Age" type="integer"/>
                    </qFields>
                </set>
            </params>
        </report>
        <report layoutID="002">
            <params>
                <sources>
                    <source name="source1" dbURL="sampledb1.com" user="user1" password="dXNlcjE=" driver="dbDriver"/>
                </sources>
                <set name="Data Set" dataSource="source1" querie="select Name, Location, MaxCapacity from SchoolsTable" >
                    <qFields>
                        <qField name="Name" type="string"/>
                        <qField name="Location" type="string"/>
                        <qField name="MaxCapacity" type="integer"/>
                    </qFields>
                </set>
            </params>
        </report>
    </reports>
</model>

Итак, я хочу создать следующий XML-файл:

<?xml version="1.0" encoding="UTF-8"?>
<report xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.23" id="1">
    <text-prop name="displayName">PersonTemplate</text-prop>
     <data-sources>
        <data-source extensionID="this.is.a.fixed.value" name="source1">
            <prop name="DriverClass">dbDriver</prop>
            <prop name="databaseURL">sampledb1.com</prop> 
            <prop name="dbUser">user1</prop>
            <encrypted-prop name="dbPassword" encryptionID="base64">dXNlcjE=</encrypted-prop>
        </data-source>
    </data-sources>
    <data-sets>
        <data-set extensionID="this.is.a.fixed.value" name="set1">
            <list-prop name="columnHints">
                <struct>
                    <prop name="columnName">Name</prop>
                    <text-prop name="displayName">Name</text-prop>
                    <text-prop name="heading">Name</text-prop>
                </struct>
                <struct>
                    <prop name="columnName">City</prop>
                    <text-prop name="displayName">City</text-prop>
                    <text-prop name="heading">City</text-prop>
                </struct>
                <struct>
                    <prop name="columnName">Age</prop>
                    <text-prop name="displayName">Age</text-prop>
                    <text-prop name="heading">Age</text-prop>
                </struct>
            </list-prop>
            <struct name="cachedMetaData">
                <list-prop name="resultSet">
                    <struct>
                        <prop name="position">1</prop>
                        <prop name="name">Name</prop>
                        <prop name="dataType">string</prop>
                    </struct>
                    <struct>
                        <prop name="position">2</prop>
                        <prop name="name">City</prop>
                        <prop name="dataType">string</prop>
                    </struct>
                    <struct>
                        <prop name="position">3</prop>
                        <prop name="name">Age</prop>
                        <prop name="dataType">integer</prop>
                    </struct>
                </list-prop>
            </struct>
            <prop name="dataSource">source1</prop>
            <list-prop name="resultSet">
                <struct>
                    <prop name="position">1</prop>
                    <prop name="name">Name</prop>
                    <prop name="dataType">string</prop>
                </struct>
                <struct>
                    <prop name="position">2</prop>
                    <prop name="name">City</prop>
                    <prop name="dataType">string</prop>
                </struct>
                <struct>
                    <prop name="position">3</prop>
                    <prop name="name">AGE</prop>
                    <prop name="dataType">integer</prop>
                </struct>
            </list-prop>
            <xml-prop name="queryText"><![CDATA[select Name, City, Age from PeopleTable]]></xml-prop>
        </data-set>
    </data-sets>
    <setup>
        <simple-master-page name="MasterPage" id="2">
            <footer>
                <text id="3">
                    <prop name="contentType">html</prop>
                    <text-prop name="content"><![CDATA[<value-of>new Date()</value-of>]]></text-prop>
                </text>
            </footer>
        </simple-master-page>
    </setup>
    <body>
        <table id="4">
            <prop name="dataSet">set1</prop>
            <list-prop name="boundDataColumns">
                <struct>
                    <prop name="name">Name</prop>
                    <text-prop name="displayName">Name</text-prop>
                    <expression name="expression" type="javascript">dataSetRow["Name"]</expression>
                    <prop name="dataType">string</prop>
                </struct>
                <struct>
                    <prop name="name">City</prop>
                    <text-prop name="displayName">City</text-prop>
                    <expression name="expression" type="javascript">dataSetRow["City"]</expression>
                    <prop name="dataType">string</prop>
                </struct>
                <structure>
                    <prop name="name">Age</prop>
                    <text-prop name="displayName">Age</text-prop>
                    <expression name="expression" type="javascript">dataSetRow["Age"]</expression>
                    <prop name="dataType">integer</prop>
                </structure>
            </list-prop>
            <column id="17"/>
            <column id="18"/>
            <column id="19"/>
            <header>
                <row id="5">
                    <cell id="6">
                        <label id="20">
                            <text-prop name="text">NameTitle</text-prop>
                        </label>
                    </cell>
                    <cell id="7">
                        <label id="21">
                            <text-prop name="text">CityTitle</text-prop>
                        </label>
                    </cell>
                    <cell id="8">
                        <label id="22">
                            <text-prop name="text">AgeTitle</text-prop>
                        </label>
                    </cell>
                </row>
            </header>
            <detail>
                <row id="9">
                    <cell id="10">
                         <data>
                            <prop name="resultSetColumn">Name</prop>
                        </data>
                    </cell>
                    <cell id="11">
                         <data>
                            <prop name="resultSetColumn">City</prop>
                        </data>
                    </cell>
                    <cell id="12">
                         <data>
                            <prop name="resultSetColumn">Age</prop>
                        </data>
                    </cell>
                </row>
            </detail>
        </table>
    </body>
</report>

Примечание: encryptionID и extensionID как в источнике данных, так и в наборе данных являются фиксированными значениями.

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

Как видите, у меня может быть много элементов layout и report во втором XML-файле. Итак, сначала я думаю, что мне нужно найти свойство displayName в первом XML-файле, а затем найти элемент layout, в котором атрибут name соответствует displayName. Затем мне нужно во втором XML-файле по атрибуту ID в элементе layout найти элемент report, который имеет то же значение в атрибуте layoutID. С этого момента я бы нашел правильный report элемент. Только сейчас я бы начал изменять / вносить дополнения в первый XML-файл. Эту часть я действительно не знаю, как это сделать. Возможно ли то, что я пытаюсь сделать?

Я знаю, что вы можете использовать функцию document для работы с 2 файлами XML, но я не знаю, как,

Мне очень нужна помощь, ребята, спасибо!

EDIT

В основном я хочу скопировать все в первый файл XML ( уже сделано ), а затем добавить к нему данные, хранящиеся во втором файле XML (тот, который начинается с элемента <model> *

UPDATE

Поскольку я хочу, чтобы вывод был дополнением к первому входному XML-файлу, я начал, как рекомендовал @Sojimanatsu, с применения преобразования идентификаторов, например, (с помощью @ TimC в сообщении по конкретной проблеме специальные символы ( XML - XSLT - экранирование специальных символов )):

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                              xmlns:xmlbirtns="http://www.eclipse.org/birt/2005/design"
                              xpath-default-namespace="http://www.eclipse.org/birt/2005/design">
    <xsl:output method="xml" omit-xml-declaration="no" indent="yes" encoding="utf-8" />
    <xsl:strip-space elements="*"/>

    <!--copy the whole input XML file-->
    <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    </xsl:template>

    <!--special treatment for setup/text-prop element-->
   <xsl:template match="report/setup/simple-master-page/footer/text/text-prop">
     <xsl:copy>
         <xsl:attribute name="name">
             <xsl:text>content</xsl:text>
         </xsl:attribute>
         <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
         <xsl:value-of select="." disable-output-escaping="yes"/>
         <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>

      </xsl:copy>
  </xsl:template> 

</xsl:stylesheet>

ПРИМЕЧАНИЕ: Код XSLT, относящийся к элементу setup / text-prop, является необязательным, поскольку мой выходной XML может иметь &lt;value-of&gt;new Date()&lt;/value-of&gt; вместо <![CDATA[<value-of>new Date()</value-of>]]>

Так что теперь я могу печатать в выходном XML именно то, что есть в первом входном XML. Теперь, как сказал @Sojimanatsu, я хочу выбрать определенные теги и редактировать их с новыми данными, поступающими из входного XML-файла sencond, а также хочу добавить новые теги / элементы, такие как элементы data-sources и data-sets, но Я не знаю, как это сделать. Я знаю, что должен использовать функцию document (), но как ??

Для начала, как я могу добавить новый элемент, <data-sources> ниже элемента <report> и перед элементом <setup>? (<data-sources> и <setup> являются братьями и сестрами)

Я попытался сделать это, добавив новое <xsl:template>:

<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                              xmlns:xmlbirtns="http://www.eclipse.org/birt/2005/design"
                              xpath-default-namespace="http://www.eclipse.org/birt/2005/design">
    <xsl:output method="xml" omit-xml-declaration="no" indent="yes" encoding="utf-8" />
    <xsl:strip-space elements="*"/>

    <!--copy the whole input XML file-->
    <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    </xsl:template>

    <!--special treatment for setup/text-prop element-->
   <xsl:template match="report/setup/simple-master-page/footer/text/text-prop">
     <xsl:copy>
         <xsl:attribute name="name">
             <xsl:text>content</xsl:text>
         </xsl:attribute>
         <xsl:text disable-output-escaping="yes">&lt;![CDATA[</xsl:text>
         <xsl:value-of select="." disable-output-escaping="yes"/>
         <xsl:text disable-output-escaping="yes">]]&gt;</xsl:text>

      </xsl:copy>
  </xsl:template>

    <xsl:template match="report/text-prop">
      <xsl:copy-of select="."/>
        <dataSources>DATA SOURCE VALUE</dataSources>
    </xsl:template>

</xsl:stylesheet>

Но я получаю этот вывод (тег dataSource находится в правильном месте, но с некоторыми атрибутами, которые я не знаю, как туда попал):

<?xml version="1.0" encoding="utf-8"?>
<report xmlns="http://www.eclipse.org/birt/2005/design" version="3.2.23" id="1">
   <text-prop name="displayName">PersonTemplate</text-prop>
   <dataSources xmlns=""
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xmlns:xmlbirtns="http://www.eclipse.org/birt/2005/design">DATA SOURCE VALUE</dataSources>
   <setup>
      <simple-master-page name="MasterPage" id="2">
         <footer>
            <text id="3">
               <prop name="contentType">html</prop>
               <text-prop name="content"><![CDATA[<value-of>new Date()</value-of>]]></text-prop>
            </text>
         </footer>
      </simple-master-page>
   </setup>
   <body>
      <table id="4">
         <column id="17"/>
         <column id="18"/>
         <column id="19"/>
         <header>
            <row id="5">
               <cell id="6">
                  <label id="20">
                     <text-prop name="text">NameTitle</text-prop>
                  </label>
               </cell>
               <cell id="7">
                  <label id="21">
                     <text-prop name="text">CityTitle</text-prop>
                  </label>
               </cell>
               <cell id="8">
                  <label id="22">
                     <text-prop name="text">AgeTitle</text-prop>
                  </label>
               </cell>
            </row>
         </header>
         <detail>
            <row id="9">
               <cell id="10"/>
               <cell id="11"/>
               <cell id="12"/>
            </row>
         </detail>
      </table>
   </body>
</report>

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

Спасибо, ребята!

Ответы [ 2 ]

0 голосов
/ 07 июля 2018

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

Кроме того, в вашем случае вы имеете дело с одним документом с элементами в пространстве имен, а второй - с элементами без пространства имен, что требует использования префиксов для двух пространств имен в документе XSLT или использования xpath-default-namespace для убедитесь, что вы выбрали правильное пространство имен.

И если вы хотите создать новые элементы результата в определенном пространстве имен, например, в вашем основном входном документе, вам нужно убедиться, что в этом пространстве имен есть элементы результата, объявив xmlns="http://www.eclipse.org/birt/2005/design" в таблице стилей.

Мне не ясно, хотите ли вы выбрать только определенные источники данных или наборы или по каким критериям выбрать только один, следующие процессы обрабатывают все указанные источники и наборы. Кроме того, только для источников данных я пытался добавить шаблоны, которые преобразуют вторичные входные данные, ориентированные на атрибуты, в целевую структуру, для наборов, которые вы должны реализовать самостоятельно, в настоящее время шаблоны по умолчанию просто копируют их.

Таблица стилей (со вторым встроенным XML, конечно, в вашем реальном коде вы можете использовать <xsl:param name="doc2" select="doc('file2.xml')"/>)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xpath-default-namespace="http://www.eclipse.org/birt/2005/design"
    xmlns="http://www.eclipse.org/birt/2005/design"
    exclude-result-prefixes="xs"
    expand-text="yes"
    version="3.0">

  <!-- second document inlined for the example,
       use param name="doc2" select="doc('file2.xml')
       instead to load external file -->
  <xsl:param name="doc2" xmlns="">
<model>
    <layouts>
        <layout ID="001" name="PersonTemplate" format="Table" nFields="3" >
            <fields>
                <field name="NameTitle"/>
                <field name="CityTitle"/>
                <field name="AgeTitle"/>
            </fields>
        </layout>
        <layout ID="002" name="SchoolTemplate" format="Table" nFields="3" >
            <fields>
                <field name="NameTitle"/>
                <field name="LocationTitle"/>
                <field name="MaxCapacityTitle"/>
            </fields>
        </layout>
    </layouts>
    <reports>
        <report layoutID="001">
            <params>
                <sources>
                    <source name="source1" dbURL="sampledb1.com" user="user1" password="dXNlcjE=" driver="dbDriver"/>
                    <source name="source2" dbURL="sampledb2.com" user="user2" password="dXNlcjI=" driver="dbDriver"/>
                </sources>
                <set name="set1" source="source1" querie="select Name, City, Age from PeopleTable" >
                    <qFields>
                        <qField name="Name" type="string"/>
                        <qField name="City" type="string"/>
                        <qField name="Age" type="integer"/>
                    </qFields>
                </set>
            </params>
        </report>
        <report layoutID="002">
            <params>
                <sources>
                    <source name="source1" dbURL="sampledb1.com" user="user1" password="dXNlcjE=" driver="dbDriver"/>
                </sources>
                <set name="Data Set" dataSource="source1" querie="select Name, Location, MaxCapacity from SchoolsTable" >
                    <qFields>
                        <qField name="Name" type="string"/>
                        <qField name="Location" type="string"/>
                        <qField name="MaxCapacity" type="integer"/>
                    </qFields>
                </set>
            </params>
        </report>
    </reports>
</model>      
  </xsl:param>

  <xsl:output indent="yes"/>

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

  <xsl:key name="layout-ref" match="layout" use="@name" xpath-default-namespace=""/>
  <xsl:key name="report-ref" match="report" use="@layoutID" xpath-default-namespace=""/>

  <xsl:template match="report/text-prop[@name = 'displayName']">
      <xsl:next-match/>
      <xsl:variable name="layout" select="key('layout-ref', ., $doc2)"/>
      <xsl:variable name="report" select="key('report-ref', $layout/@ID, $doc2)"/>
      <dataSources>
          <xsl:apply-templates select="$report//source" xpath-default-namespace=""/>
      </dataSources>
      <data-sets>
          <xsl:apply-templates select="$report//set" xpath-default-namespace=""/>
      </data-sets>

  </xsl:template>

  <xsl:template match="source" xpath-default-namespace="">
      <data-source extensionID="this.is.a.fixed.value" name="{@name}">
          <xsl:apply-templates select="@* except @name"/>
      </data-source>
  </xsl:template>

  <xsl:template match="source/@dbURL" xpath-default-namespace="">
      <prop name="databaseURL">{.}</prop>
  </xsl:template>

  <xsl:template match="source/@user" xpath-default-namespace="">
      <prop name="dbUser">{.}</prop>
  </xsl:template>  

  <xsl:template match="source/@driver" xpath-default-namespace="">
      <prop name="DriverClass">{.}</prop>
  </xsl:template>

  <xsl:template match="source/@password" xpath-default-namespace="">
      <encrypted-prop name="dbPassword" encryptionID="base64">{.}</encrypted-prop>
  </xsl:template>

  <xsl:template match="set" xpath-default-namespace="">
      <data-set extensionID="this.is.a.fixed.value" name="{@name}">
          <xsl:apply-templates select="@*, *"/>
      </data-set>
  </xsl:template>

</xsl:stylesheet>

Онлайн-образец на https://xsltfiddle.liberty -development.net / bFDb2Ck .

0 голосов
/ 06 июля 2018

Я думаю, что вы ищете эту функцию в основном.

функция document ()

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

...