Как я могу скопировать структуру XML в другую за исключением некоторых атрибутов или узлов - PullRequest
1 голос
/ 01 декабря 2010

со структурой XML ниже:

<root foo1="bar1" foo2="bar2" foo3="bar3">
    <foo1 foo1="bar1" />

    <data>
        <foo1>bar1</foo1>
        <foo2>bar2</foo2>
        <foo3>bar3</foo3>
    </data>
</root>

Я хотел бы скопировать эту структуру XML в другую, с некоторыми исключениями для атрибутов и / или имен узлов (), и получить следующий результат, используя XSLT 1.0:

<root foo1="bar1" foo2="bar2">    
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

Мои правила:

1) Скопируйте все корневые атрибуты, кроме foo3

2) Скопируйте каждый дочерний элемент nodes(), за исключением тех, которые названы foo1 и foo2

Моя фактическая таблица стилей XSL.Мне удалось заставить работать ограничение корневых атрибутов:

<xsl:template match="root">
    <root>
        <xsl:for-each select="./@*">
            <xsl:variable name="name" select="name()" />

            <xsl:if test="name() != 'foo3'">
                <xsl:attribute name="{$name}"><xsl:value-of select="." /></xsl:attribute>
            </xsl:if>
        </xsl:for-each>


    </root>
</xsl:template>

Кроме того, еще один сложный вопрос: что, если я хочу динамически сопоставлять свои атрибуты и узлы.Я хотел бы указать на стороне сервера, какие атрибуты и nodes() я хотел бы удалить.Это похоже на генерацию строки, которая затем используется в <xsl:if>.Я не знаю, возможно ли это вообще.

Спасибо.

Ответы [ 3 ]

1 голос
/ 01 декабря 2010

Я хотел бы скопировать этот XML структура в другую с некоторыми исключение [...]

Мои правила:

1) Копировать все корневые атрибуты, кроме foo3

2) Скопируйте все дочерние узлы (), если только названные foo1 и foo2

Обновление от комментариев :

Привет, это почти работа. Кроме этого data / foo1 должен быть скопирован

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

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="root/@foo3|root/foo1|foo2"/>
</xsl:stylesheet>

Выход:

<root foo1="bar1" foo2="bar2">
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

Примечание : перезапись правила идентификации пустыми шаблонами

С именами узлов в параметре, эта таблица стилей:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:param name="pStrip" select="'root/@foo3|root/foo1|foo2'"/>
    <xsl:template match="node()|@*" name="identity">
        <xsl:param name="pStripPaths" select="concat($pStrip,'|')"/>
        <xsl:param name="pNodePath">
            <xsl:call-template name="path"/>
            <xsl:text>|</xsl:text>
        </xsl:param>
        <xsl:variable name="vStripPath"
                      select="substring-before($pStripPaths,'|')"/>
        <xsl:choose>
            <xsl:when test="not($pStripPaths)">
                <xsl:copy>
                    <xsl:apply-templates select="node()|@*"/>
                </xsl:copy>
            </xsl:when>
            <xsl:when test="contains($pNodePath,concat('/',$vStripPath,'|'))"/>
            <xsl:otherwise>
                <xsl:call-template name="identity">
                    <xsl:with-param name="pStripPaths"
                                    select="substring-after($pStripPaths,'|')"/>
                    <xsl:with-param name="pNodePath" select="$pNodePath"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template match="node()" name="path" mode="path">
        <xsl:apply-templates select="parent::*" mode="path"/>
        <xsl:value-of select="concat('/',
                                     substring('@',
                                               1 div (count(.|../@*)
                                                      = count(../@*))),
                                     name())"/>
    </xsl:template>
</xsl:stylesheet>

Выход:

<root foo1="bar1" foo2="bar2">
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

Примечание : В XML имя элемента относится к схеме, в основном определяющей положение иерархии, но ваше дело не в этом.

Редактировать : просто для удовольствия, решение XSLT 2.0:

<xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     xmlns:local="http://localhost">
    <xsl:param name="pStrip" select="'root/@foo3|root/foo1|foo2'"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="*[local:match($pStrip,.)]|@*[local:match($pStrip,.)]"/>
    <xsl:function name="local:match" as="xs:boolean">
        <xsl:param name="pStripPaths" as="xs:string"/>
        <xsl:param name="pNode" as="item()"/>
        <xsl:variable name="vNodePath"
                      select="string-join(($pNode
                                            /ancestor::node()
                                             /name(),
                                           if ($pNode instance of attribute())
                                           then concat('@',name($pNode))
                                           else name($pNode)),
                                           '/')"/>
        <xsl:sequence select="some $path in tokenize($pStripPaths,'\|')
                                  satisfies ends-with($vNodePath,
                                                      concat('/',$path))"/>
    </xsl:function>
</xsl:stylesheet>

Редактировать 2 : Все таблицы стилей следуют одному и тому же строковому шаблону.

1 голос
/ 01 декабря 2010

Вы можете использовать XPath для упрощения вашего выбора:

<xsl:for-each select="./@*[not(name()='foo3')]">

Тогда вам не нужно проверять имена.Вы можете сделать аналогичную вещь для элементов:

*[not(name()='foo2')]
0 голосов
/ 02 декабря 2010

Это преобразование принимает параметр с выражениями XPath для всех элементов / атрибутов, которые должны быть удалены :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:msxsl="urn:schemas-microsoft-com:xslt"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pDeletes">
  <element>/root/data/foo2</element>
  <element>/root/foo1</element>
  <attribute>/root/@foo3</attribute>
 </xsl:param>

 <xsl:variable name="vDeletes"
  select="msxsl:node-set($pDeletes)"/>

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

 <xsl:template match="*">
  <xsl:variable name="vPath">
   <xsl:apply-templates select="." mode="buildPath"/>
  </xsl:variable>

  <xsl:if test="not(string($vPath) = $vDeletes/element)">
    <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>

 <xsl:template match="@*">
  <xsl:variable name="vPath">
   <xsl:apply-templates select="." mode="buildPath"/>
  </xsl:variable>

  <xsl:if test="not(string($vPath) = $vDeletes/attribute)">
    <xsl:call-template name="identity"/>
  </xsl:if>
 </xsl:template>

 <xsl:template match="*" mode="buildPath">
  <xsl:for-each select="ancestor-or-self::*">
    <xsl:value-of select="concat('/',name())"/>

    <xsl:variable name="vprecSiblings"
     select="count(preceding-sibling::*[name()=name(current())])"/>

    <xsl:if test="$vprecSiblings">
     <xsl:value-of select="concat('[', $vprecSiblings, ']')"/>
    </xsl:if>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="@*" mode="buildPath">
  <xsl:variable name="vParentPath">
    <xsl:apply-templates select=".." mode="buildPath"/>
  </xsl:variable>

  <xsl:value-of select="concat($vParentPath, '/@', name())"/>
 </xsl:template>
</xsl:stylesheet>

при применении к предоставленному документу XML :

<root foo1="bar1" foo2="bar2" foo3="bar3">
    <foo1 foo1="bar1" />
    <data>
        <foo1>bar1</foo1>
        <foo2>bar2</foo2>
        <foo3>bar3</foo3>
    </data>
</root>

желаемый, правильный результат получается :

<root foo1="bar1" foo2="bar2">
    <data>
        <foo1>bar1</foo1>
        <foo3>bar3</foo3>
    </data>
</root>

Примечание :

  1. Каждое выражение XPath, которое идентифицирует удаляемый узел, является строковым значением element или attribute дочернего элемента глобального, предоставляемого извне параметра .Этапы расположения любого такого выражения содержат имя элемента, а в предикатной части - его порядковый номер среди всех идентично названных братьев и сестер.Если это первый элемент, предикат не должен быть задан.Для атрибутов последний шаг расположения содержит ровно "@"AttributeName, где AttributeName - имя атрибута.

  2. Функция расширения xxx:node-set() здесь только для демонстрацииpurpuses .На практике параметр $pDeletes будет предоставлен извне, и нет необходимости применять к нему функцию xxx:node-set().

  3. Все выражения XPath могут быть представлены водна строка (используя лучший разделитель, чем оператор объединения | XPath :)), но это может быть менее эффективно, чем иметь каждое выражение в отдельном элементе. В последнем случае элементы или атрибуты можно искать отдельно, а такжеесли эффективность имеет значение, выражения могут быть проиндексированы, скажем, по имени элемента или атрибута, что обеспечивает очень быстрое сублинейное время поиска.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...