Создание допустимого XML для схемы с помощью XSLT - PullRequest
0 голосов
/ 08 июля 2010

Я сортирую список элементов по типу, как определено в моей схеме. Я знаю, что XSLT может проверять соответствие определенной схеме, но я хочу проверить, что моя операция (в данном случае копия) действительна, прежде чем я это сделаю.

Упрощенный код:

Входящие данные:

<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
    <sch:attr1>val1</sch:attr1>
    <sch:attr2>val2</sch:attr2>
    <sch:attr3>val3</sch:attr3>
    <sch:attr4>val4</sch:attr4>
</sch:foo>

Желаемые исходящие данные:

<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
    <sch:bar>
        <sch:attr1>val1</sch:attr1>
        <sch:attr2>val2</sch:attr2>
    </sch:bar>
    <sch:stuff>
        <sch:attr3>val3</sch:attr3>
        <sch:attr4>val4</sch:attr4>
    </sch:stuff>
</sch:fooOut>

Где-то в файле схемы:

<complexType name="fooOut">
    <sequence>
        <!-- ... -->
        <element name="bar">
            <complexType>
                <sequence>
                    <element name="attr1" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                    <element name="attr2" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                </sequence>
            </complexType>
        </element>
        <element name="stuff">
            <complexType>
                <sequence>
                    <element name="attr3" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                    <element name="attr4" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                </sequence>
            </complexType>
        </element>
    </sequence>
</complexType>

(Я только учусь работать с .xsd, так сказать словами: только attr1 и attr2 могут входить в bar, и только attr3 и attr4 могут входить stuff)

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

Вся помощь приветствуется и спасибо!

EDIT

@ Код Алехандро работает для вышеуказанного базового псевдокода, но у меня возникают проблемы с его реализацией в моих файлах, которые являются более сложными. По этой причине я добавляю более сложный пример:

Входящие данные

<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
    <sch:nesting>
        <sch:myGroup>
            <sch:mustHaveData>asdf</sch:mustHaveData>

            <sch:attr1>val1</sch:attr1>
            <sch:attr2>val2</sch:attr2>
            <sch:attr3>val3</sch:attr3>
            <sch:attr4>val4</sch:attr4>
        </sch:myGroup>
        <sch:myGroup>
            <sch:mustHaveData>asdf2</sch:mustHaveData>

            <sch:attr1>val5</sch:attr1>
            <sch:attr2>val6</sch:attr2>
            <sch:attr3>val7</sch:attr3>
            <sch:attr4>val8</sch:attr4>
        </sch:myGroup>
    </sch:nesting>
</sch:foo>

Требуемые исходящие данные:

<?xml version="1.0"?>
<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val1</sch:attr1>
            <sch:attr2>val2</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val3</sch:attr3>
            <sch:attr4>val4</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val5</sch:attr1>
            <sch:attr2>val6</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val7</sch:attr3>
            <sch:attr4>val8</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
</sch:fooOut>

Где-то в файлах схемы: (и несколько точнее, чем в прошлый раз)

<complexType name="anotherGroup">
    <sequence>
        <element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
        <element name="bar" type="barListType" minOccurs="0" maxOccurs="1" />
        <element name="stuff" type="stuffListType" minOccurs="0" maxOccurs="1" />
    </sequence>
</complexType>

<!-- in another .xsd -->
<complexType name="barListType">
    <group ref="barGroup" maxOccurs="unbounded" />
</complexType>

<complexType name="stuffListType">
    <group ref="stuffGroup" maxOccurs="unbounded" />
</complexType>

<!-- in yet another .xsd -->
<group name="barGroup">
    <choice>
        <element name="attr1" type="blah1" minOccurs="0" maxOccurs="1" />
        <element name="attr2" type="blah2" minOccurs="0" maxOccurs="1" />
        <!-- etc -->
    </choice>
</group>

<group name ="stuffGroup">
    <choice>
        <element name="attr3" type="blah3" minOccurs="0" maxOccurs="1" />
        <element name="attr4" type="blah4" minOccurs="0" maxOccurs="1" />
        <!-- etc -->
    </choice>
</group>

Наконец-то мой xsl-файл

    <xsl:output method="xml" encoding="UTF-8" />

    <xsl:param name="schema-name" select="'myXsd.xsd'" />
    <xsl:template match="/">
        <xsl:apply-templates select="document($schema-name)/xs:complexType[@*]" />
        <xsl:apply-templates select="sch:nesting"/>
    </xsl:template>

    <xsl:template match="sch:nesting/xs:element[xs:complexType]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:apply-templates />
        </xsl:element>
    </xsl:template>

    <xsl:template match="sch:nesting/xs:element[not(xs:complexType)]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:value-of select="/*/sch:*[name()=current()/@name or
                                  substring-after(name(),':')=current()/@name]"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="sch:nesting">
        <xsl:element name="anotherGroup">
            <xsl:element name="name">
                <!-- Whatever -->
            </xsl:element>

            <xsl:apply-templates /> <!-- I want to drop the data here, but this is definitely wrong -->

        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Еще раз спасибо за помощь!

РЕДАКТИРОВАТЬ 2

Итак, есть одно незначительное изменение, которое я забыл внести в свой файл данных. Все остальное должно быть таким же, за исключением макета, который вложен так:

<?xml version="1.0"?>
<sch:foo xmlns:sch="http://www.whatever.com/schema">
    <sch:nesting>
        <sch:myGroup>
            <inner1>
                <sch:mustHaveData>asdf</sch:mustHaveData>

                <sch:attr1>val1</sch:attr1>
            </inner1>
            <inner2>
                <sch:attr2>val2</sch:attr2>
                <sch:attr3>val3</sch:attr3>
                <sch:attr4>val4</sch:attr4>
            </inner2>
        </sch:myGroup>
        <sch:myGroup>
            <inner1>
                <sch:mustHaveData>asdf2</sch:mustHaveData>

                <sch:attr1>val5</sch:attr1>
            </inner1>
            <inner2>
                <sch:attr2>val6</sch:attr2>
                <sch:attr3>val7</sch:attr3>
                <sch:attr4>val8</sch:attr4>
            </inner2>
        </sch:myGroup>
    </sch:nesting>
</sch:foo>

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

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

Еще раз спасибо! Вы действительно спасатель!

РЕДАКТИРОВАТЬ 3

Я выяснил желаемый результат. Я изменил строки, которые говорили

<xsl:copy-of select="*[local-name()=document($schema-name)/*/*

до

<xsl:copy-of select="*/*[local-name()=document($schema-name)/*/*

Что дало мне дополнительный бит, который мне был нужен.

Ответы [ 2 ]

3 голосов
/ 09 июля 2010

Схема ("schema.xs"):

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <element name="fooOut">
        <complexType name="fooOut">
            <sequence>
                <element name="bar">
                    <complexType>
                        <sequence>
                            <element name="attr1" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                            <element name="attr2" type="sch:myBarType" minOccurs="0" maxOccurs="unbounded"/>
                        </sequence>
                    </complexType>
                </element>
                <element name="stuff">
                    <complexType>
                        <sequence>
                            <element name="attr3" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                            <element name="attr4" type="sch:myStuffType" minOccurs="0" maxOccurs="unbounded"/>
                        </sequence>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
</schema>

Ввод:

<sch:foo xmlns:sch="http://www.whatever.com/schema">   
    <sch:attr1>val1</sch:attr1>   
    <sch:attr2>val2</sch:attr2>   
    <sch:attr3>val3</sch:attr3>   
    <sch:attr4>val4</sch:attr4>   
</sch:foo> 

Таблица стилей:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:sch="http://www.whatever.com/schema">
    <xsl:param name="schema-name" select="'schema.xs'"/>
    <xsl:variable name="input" select="/"/>
    <xsl:template match="/">
        <xsl:apply-templates select="document($schema-name)/node()"/>
    </xsl:template>
    <xsl:template match="xs:element[xs:complexType]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    <xsl:template match="xs:element[not(xs:complexType)]">
        <xsl:element name="{@name}" namespace="http://www.whatever.com/schema">
            <xsl:value-of select="$input/*/sch:*[name()=current()/@name or
                                        substring-after(name(),':')=current()/@name]"/>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

Результат:

<?xml version="1.0" encoding="UTF-16"?>
<fooOut xmlns="http://www.whatever.com/schema">
    <bar>
        <attr1>val1</attr1>
        <attr2>val2</attr2>
    </bar>
    <stuff>
        <attr3>val3</attr3>
        <attr4>val4</attr4>
    </stuff>
</fooOut>

Примечание: Это решение XSLT 1.0, но я думаю, что это можно сделать лучше с XSLT 2.0.- Кроме того, если схема является более подходящей (определения элементов и определения типов), этоМетод может работать с ключами.Другой метод (управляемый вводом, а не управляемый схемой), хотя и может быть более сложным, также может быть реализован, но у меня заканчивается время.

Редактировать : Теперь предположим, что эти схемы

schemaA.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <include schemaLocation="schemaB.xsd"/>
    <element name="fooOut" type="fooOut"/>
    <complexType name="fooOut">
        <element name="anotherGroup" type="anotherGroup" minOccurs="0" maxOccurs="unbounded"/>
    </complexType>
    <complexType name="anotherGroup">
        <sequence>
            <element name="name" type="xs:string" minOccurs="1" maxOccurs="1" />
            <element name="bar" type="barListType" minOccurs="0" maxOccurs="1" />
            <element name="stuff" type="stuffListType" minOccurs="0" maxOccurs="1" />
        </sequence>
    </complexType>
</schema>

schemaB.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <include schemaLocation="schemaC.xsd"/>
    <complexType name="barListType">
        <group ref="barGroup" maxOccurs="unbounded" />
    </complexType>
    <complexType name="stuffListType">
        <group ref="stuffGroup" maxOccurs="unbounded" />
    </complexType>
</schema>

schemaC.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.whatever.com/schema">
    <group name="barGroup">
        <choice>
            <element name="attr1" type="blah1" minOccurs="0" maxOccurs="1" />
            <element name="attr2" type="blah2" minOccurs="0" maxOccurs="1" />
            <!-- etc -->
        </choice>
    </group>
    <group name ="stuffGroup">
        <choice>
            <element name="attr3" type="blah3" minOccurs="0" maxOccurs="1" />
            <element name="attr4" type="blah4" minOccurs="0" maxOccurs="1" />
            <!-- etc -->
        </choice>
    </group>
</schema>

Эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xsl:output method="xml" encoding="UTF-8" />
    <xsl:param name="schema-name" select="'schemaA.xsd'" />
    <xsl:variable name="input" select="/*/*/*" />
    <xsl:variable name="root" select="document($schema-name)/*/xs:element[not(..//xs:element/@ref = @name)]" />
    <xsl:variable name="uri" select="$root/../@targetNamespace" />
    <xsl:template match="/" name="root">
        <xsl:param name="schema" select="$root/../*"/>
        <xsl:choose>
            <xsl:when test="$schema[self::xs:include]">
                <xsl:call-template name="root">
                    <xsl:with-param name="schema" select="$schema[not(self::xs:include)]|document($schema[self::xs:include]/@schemaLocation)/*/*"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="$root">
                    <xsl:with-param name="schema" select="$schema"/>
                    <xsl:with-param name="input" select="$input"/>
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="xs:element">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:variable name="complex" select="xs:complexType|
                                             $schema[self::xs:complexType][@name=current()/@type]"/>
        <xsl:element name="{@name|@ref}" namespace="{$uri}">
            <xsl:choose>
                <xsl:when test="$complex">
                    <xsl:apply-templates select="$complex">
                        <xsl:with-param name="schema" select="$schema"/>
                        <xsl:with-param name="input" select="$input"/>
                    </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:value-of select="$input[local-name()=current()/@name and
                                  namespace-uri()=$uri]"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:element>
    </xsl:template>
    <xsl:template match="xs:group[@ref]">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:apply-templates select="$schema[self::xs:group][@name=current()/@ref]">
            <xsl:with-param name="schema" select="$schema"/>
            <xsl:with-param name="input" select="$input"/>
        </xsl:apply-templates>
    </xsl:template>
    <xsl:template match="xs:element[@name='name']" priority="1">
        <xsl:element name="{@name}" namespace="{$uri}">foobar</xsl:element>
    </xsl:template>
    <xsl:template match="xs:element[@maxOccurs='unbounded']">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:variable name="current" select="."/>
        <xsl:for-each select="$input">
            <xsl:element name="{$current/@name}" namespace="{$uri}">
                <xsl:apply-templates select="$schema[self::xs:complexType][@name=$current/@type]">
                    <xsl:with-param name="schema" select="$schema"/>
                    <xsl:with-param name="input" select="./*"/>
                </xsl:apply-templates>
            </xsl:element>
        </xsl:for-each>
    </xsl:template>
    <xsl:template match="*">
        <xsl:param name="schema"/>
        <xsl:param name="input"/>
        <xsl:apply-templates>
            <xsl:with-param name="schema" select="$schema"/>
            <xsl:with-param name="input" select="$input"/>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

Результат:

<fooOut xmlns="http://www.whatever.com/schema">
    <anotherGroup>
        <name>foobar</name>
        <bar>
            <attr1>val1</attr1>
            <attr2>val2</attr2>
        </bar>
        <stuff>
            <attr3>val3</attr3>
            <attr4>val4</attr4>
        </stuff>
    </anotherGroup>
    <anotherGroup>
        <name>foobar</name>
        <bar>
            <attr1>val5</attr1>
            <attr2>val6</attr2>
        </bar>
        <stuff>
            <attr3>val7</attr3>
            <attr4>val8</attr4>
        </stuff>
    </anotherGroup>
</fooOut>

Примечание: Это работает, но ваши вторые вопросы (илипроблема) показывает, что нет общей таблицы стилей.Зачем?Потому что с XSLT вы должны привязать вход (с хорошо известной схемой) к выходу (с хорошо известной схемой тоже).Таким образом, эта конкретная таблица стилей может выполнить эту работу:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sch="http://www.whatever.com/schema">
    <xsl:output method="xml" encoding="UTF-8" />
    <xsl:param name="schema-name" select="'schemaC.xsd'" />
    <xsl:template match="sch:foo">
        <sch:fooOut>
            <xsl:apply-templates/>
        </sch:fooOut>
    </xsl:template>
    <xsl:template match="sch:myGroup">
        <sch:anotherGroup>
            <sch:name>foobar</sch:name>
            <sch:bar>
                <xsl:copy-of select="*[local-name()=document($schema-name)/*/*[@name='barGroup']//@name]" />
            </sch:bar>
            <sch:stuff>
                <xsl:copy-of select="*[local-name()=document($schema-name)/*/*[@name='stuffGroup']//@name]" />
            </sch:stuff>
        </sch:anotherGroup>
    </xsl:template>
</xsl:stylesheet>

Результат:

<sch:fooOut xmlns:sch="http://www.whatever.com/schema">
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val1</sch:attr1>
            <sch:attr2>val2</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val3</sch:attr3>
            <sch:attr4>val4</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
    <sch:anotherGroup>
        <sch:name>foobar</sch:name>
        <sch:bar>
            <sch:attr1>val5</sch:attr1>
            <sch:attr2>val6</sch:attr2>
        </sch:bar>
        <sch:stuff>
            <sch:attr3>val7</sch:attr3>
            <sch:attr4>val8</sch:attr4>
        </sch:stuff>
    </sch:anotherGroup>
</sch:fooOut>
0 голосов
/ 08 июля 2010

Не уверен, что вы ищете здесь. XSLT будет копировать из исходного документа в целевой документ, как вы говорите. Например:

<sch:fooOut>
  <sch:bar>
    <xsl:copy>
      <sch1:attr1>
      <sch1:attr2>
    </xsl:copy>
  </sch:bar>
</sch:fooOut>

Какую проверку вы ищете? Возможно, вам лучше использовать что-то другое, чем XSLT, если вы ищете более динамичное решение.

...