Проверьте, существует ли * выходной * узел - PullRequest
2 голосов
/ 26 января 2012

Я хочу объединить два файла A.xml и map.xml с элементами "Node" в соответствии со следующим правилом (узлы различаются @Name):

  1. Если элемент в map.xml имеет атрибут Src, элемент из карты следует скопировать в выходной файл
  2. Если элемент существует в A и map и НЕ имеет @Src, его следует скопировать из A
  3. Если элемент существует в A, но отсутствует на карте, его следует игнорировать (с предупреждением)
  4. Если элемент существует в карте, но не в A, должен быть сгенерирован (пустой) элемент

Пример:

map.xml:

<?xml version="1.0"?>

<Node Name="ParentNode">
    <Node Name="Child1" Src="Child1/"/>
    <Node Name="Child2" Src="Child2/"/>
    <Node Name="Child3" Src="Child3/"/>

    <Node Name="Child4">
        <Node Name="Child4_Sub1" />
        <Node Name="Child4_Sub2" Src="Child4_Sub2/"/>
    </Node>

    <Node Name="Child5" />
</Node>

a.xml:

<Node Name="ParentNode">
    <Node Name="Child4">
        <Node Name="Child4">
            <Node Name="Child4_Sub1">
                <!-- Here are many other elements -->
            </Node>
        </Node>
    </Node> 
    <!-- Here are many other elements -->
    <Node Name="Child1">
        <!-- Here are many other elements -->
    </Node>
    <!-- Here are many other elements -->

    <Node Name="ChildFoo">
        <!-- Here are many other elements -->
    </Node>
</Node>

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

<Node Name="ParentNode">
    <Node Name="Child4">
        <Node Name="Child4">
            <Node Name="Child4_Sub1">
                <!-- Here are many other elements -->
            </Node>
            <Node Name="Child4_Sub2" />
        </Node>
    </Node> 
    <!-- Here are many other elements -->
    <Node Name="Child1" Src="Child1" />
    <!-- Here are many other elements -->

    <Node Name="Child2" Src="Child2" />
    <Node Name="Child3" Src="Child3" />
</Node>

Мой XSLT-скрипт:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0">
    <xsl:param name="mapFile" required="yes"/>

    <xsl:variable name="MapDiagram" select="document($mapFile,/*)"/>
    <xsl:variable name="CurrentDocument" select="/" />

    <!-- handle Node elements in A.xml -->
    <xsl:template match="Node">
        <xsl:variable name="MyName" select="@Name"/>
        <xsl:choose>
            <xsl:when test="$MapDiagram//Node[@Name = $MyName]">
                <xsl:choose>
                    <xsl:when test="$MapDiagram//Node[@Name = $MyName]/@Src">
                        <xsl:copy-of select="$MapDiagram//Node[@Name = $MyName]"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <Node Name="{@Name}" Type="{@Type}">
                            <xsl:apply-templates/>
                            <xsl:apply-templates select="$MapDiagram//Node[@Name = $MyName]" mode="MapDiagram" />
                        </Node>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:message terminate="no">WARNING: Node "<xsl:value-of select="@Name"/>" not found in map file, ignoring</xsl:message>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <!-- handle Node elements from map file -->
    <xsl:template match="Node" mode="MapDiagram">
        <xsl:variable name="MyName" select="@Name"/>
        <xsl:choose>
            <xsl:when test="not($CurrentDocument//Node[@Name = $MyName])">
                <xsl:copy-of select="."/>
            </xsl:when>
            <xsl:otherwise>
                    <xsl:apply-templates mode="MapDiagram" />
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <!-- Copy all other elements in between -->
    <xsl:template match="*[name() != 'Node']">
        <xsl:copy-of select="."/>
    </xsl:template>
</xsl:stylesheet>

Скрипт работает отлично. Он обрабатывает A.xml и ищет каждый элемент Node в map.xml. Поскольку узлы @Src и non-@ Src могут быть смешаны, он вызывается рекурсивно.

Однако этот скрипт генерирует:

<Node Name="ParentNode">
    <Node Name="Child4">
        <Node Name="Child4">
            <Node Name="Child4_Sub1">
                <!-- Here are many other elements -->
            </Node>
            <Node Name="Child4_Sub2" />
        </Node>
    </Node> 
    <!-- Here are many other elements -->
    <Node Name="Child1" Src="Child1" />
    <!-- Here are many other elements -->

    <Node Name="Child2" Src="Child2" />
    <Node Name="Child3" Src="Child3" />
    <Node Name="Child4_Sub2" />
</Node>

Итак, Child4_Sub2 генерируется дважды, что не имеет смысла, так как Child4_Sub2 в любом случае будет нуждаться в Child4 в качестве родителя! Но до сих пор я не нашел способа предотвратить печать этого элемента.

У вас есть какие-нибудь подсказки?

С уважением, DIVB

Ответы [ 3 ]

5 голосов
/ 26 января 2012

Изменение:

      <xsl:apply-templates mode="MapDiagram" select=
        "$MapDiagram//Node[@Name = $MyName]"/>

до:

    <xsl:if test="not(@Name = ancestor::Node/@Name)">
      <xsl:apply-templates mode="MapDiagram" select=
        "$MapDiagram//Node[@Name = $MyName]"/>
    </xsl:if>

Вот полное решение :

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="mapFile"
        select="'file:///c:/temp/delete/map.xml'"/>

        <xsl:variable name="MapDiagram" select="document($mapFile,/*)"/>
        <xsl:variable name="CurrentDocument" select="/" />

        <!-- handle Node elements in A.xml -->
        <xsl:template match="Node">
            <xsl:variable name="MyName" select="@Name"/>
            <xsl:choose>
                <xsl:when test="$MapDiagram//Node[@Name = $MyName]">

                    <xsl:choose>
                        <xsl:when test="$MapDiagram//Node[@Name = $MyName]/@Src">
                            <xsl:copy-of select="$MapDiagram//Node[@Name = $MyName]"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <Node Name="{@Name}" Type="{@Type}">
                                <xsl:apply-templates/>

                                <xsl:if test="not(@Name = ancestor::Node/@Name)">
                                  <xsl:apply-templates mode="MapDiagram" select=
                                     "$MapDiagram//Node[@Name = $MyName]"
                                  />
                                </xsl:if>
                            </Node>
                        </xsl:otherwise>
                    </xsl:choose>

                </xsl:when>
                <xsl:otherwise>
                    <xsl:message terminate="no">WARNING: Node "<xsl:value-of select="@Name"/>" not found in map file, ignoring</xsl:message>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>

        <!-- handle Node elements from map file -->
        <xsl:template match="Node" mode="MapDiagram">
            <xsl:variable name="MyName" select="@Name"/>
            <xsl:choose>
                <xsl:when test="not($CurrentDocument//Node[@Name = $MyName])">
                    <xsl:copy-of select="."/>
                </xsl:when>
                <xsl:otherwise>
                        <xsl:apply-templates mode="MapDiagram" />
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>

        <!-- Copy all other elements in between -->
        <xsl:template match="*[name() != 'Node']">
            <xsl:copy-of select="."/>
        </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к предоставленному документу XML :

<Node Name="ParentNode">
    <Node Name="Child4">
        <Node Name="Child4">
            <Node Name="Child4_Sub1"/>
        </Node>
    </Node>

    <Node Name="Child1"/>

    <Node Name="ChildFoo"/>
</Node>

и предоставляемый файл «map.xml» at C:\temp\delete\map.xml:

<Node Name="ParentNode">
    <Node Name="Child1" Src="Child1/"/>
    <Node Name="Child2" Src="Child2/"/>
    <Node Name="Child3" Src="Child3/"/>
    <Node Name="Child4">
        <Node Name="Child4_Sub1" />
        <Node Name="Child4_Sub2" Src="Child4_Sub2/"/>
    </Node>
    <Node Name="Child5" />
</Node>

Требуемый результат (не содержащий нежелательных повторений) получается :

<Node Name="ParentNode" Type="">
   <Node Name="Child4" Type="">
      <Node Name="Child4" Type="">
         <Node Name="Child4_Sub1" Type=""/>
      </Node>
      <Node Name="Child4_Sub2" Src="Child4_Sub2/"/>
   </Node>
   <Node Name="Child1" Src="Child1/"/>
   <Node Name="Child2" Src="Child2/"/>
   <Node Name="Child3" Src="Child3/"/>
   <Node Name="Child4_Sub2" Src="Child4_Sub2/"/>
   <Node Name="Child5"/>
</Node>

Общее примечание : предоставленный код довольно сложный и запутанный - с ним могут быть другие логические проблемы. Языковые функции XSLT 2.0 не используются - по сути, это решение XSLT 1.0. Было бы неплохо переписать ваш код в лучшую форму.

3 голосов
/ 26 января 2012

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

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

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

0 голосов
/ 26 января 2012

Кроме того, вы можете использовать VBScript / JScript внутри XSL для создания вещей, которые вы не можете сделать с помощью самого XSL. Вы можете создать функцию VBS, которая проверяет, существует ли Row, и возвращает True или False. XSL проверит результат функции и, если он ложный, пропустит генерацию элемента и будет двигаться дальше. Конечно, вам нужно управлять добавленными строками в скрипте.

Пример VBScript , Пример JS

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