Быстрый вопрос: есть ли способ увеличить предикат XPATH, используя переменную, например итерацию по массиву в C? Например, / XPATH / element [i]

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

Проблема, которую я пытаюсь решить, заключается в том, что каждая таблица может иметь несколько строк, которые выводятся в XML как одноуровневые узлы с одинаковыми именами. В любой таблице может быть бесконечное количество строк, поэтому я пытаюсь использовать for-each с XPATH имени таблицы для обработки каждой строки. Это работает, но когда я пытаюсь использовать функцию документа с XPATH с предикатом для первого XPATH, а затем для следующего XPATH, а затем для следующего, я не знаю, как это сделать. Я могу получить доступ только к первому XPATH. Мне нужен способ доступа к следующему XPATH на каждой итерации for-each. Есть ли что-нибудь, что может увеличивать каждый цикл и использовать предикат для указания на следующий XPATH?

XML-код ниже - это образец, который я использую для тестирования, он называется DB.xml:

<?xml version="1.0"?>
<dataset>
 <rtbp>
  <cfmtype>dog</cfmtype>
  <cfmid>1</cfmid>
 </rtbp>
 <rtbp>
  <cfmtype>cat</cfmtype>
  <cfmid>2</cfmid>
 </rtbp>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>1</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>1</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>2</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>2</RTBP__CFMID>
 </FunctionSet>
</dataset>

Ниже представлен XSL, который я использую:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>

 <xsl:template match="/">
  <xsl:for-each select="dataset/rtbp">
   <xsl:element name="RTBP">
    <xsl:attribute name="CFMtype">
     <xsl:value-of select="document('DB.xml')/dataset/rtbp[1]/cfmtype" />
    </xsl:attribute>
    <xsl:attribute name="CFMid">
     <xsl:value-of select="document('DB.xml')/dataset/rtbp[1]/cfmid" />
    </xsl:attribute>
    <xsl:text>&#xa;</xsl:text>

    <xsl:for-each select="/dataset/FunctionSet">
     <xsl:element name="FunctionSet">
      <xsl:attribute name="RTBP__CFMid">
       <xsl:value-of select="document('DB.xml')/dataset/FunctionSet[1]/FUNCTIONSET__IDENTIFIER" />
      </xsl:attribute>
      <xsl:attribute name="RTBP_FunctionSet">
       <xsl:value-of select="document('DB.xml')/dataset/FunctionSet[1]/RTBP__CFMID" />
      </xsl:attribute>
     </xsl:element>
     <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>

   </xsl:element>
   <xsl:text>&#xa;</xsl:text>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

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

Ожидаемый результат ниже:

<?xml version="1.0"?>
<RTBP CFMtype="dog" CFMid="1">
  <FunctionSet RTBP__CFMid="1" RTBP_FunctionSet="1"/>
</RTBP>
<RTBP CFMtype="cat" CFMid="2">
  <FunctionSet RTBP__CFMid="2" RTBP_FunctionSet="2"/>
</RTBP>

Как вы можете сказать, вторая таблица (FunctionSet) является дочерней по отношению к первой (RTBP), следовательно, for-each внутри for-each. Мне нужен метод, который поместит первую строку FunctionSet в первую строку RTBP, а также для вторых строк.

Я новичок в вопросах XML, XSL и публикации.

2
user3635688 14 Май 2014 в 12:49

3 ответа

Лучший ответ

Для всех, у кого было так мало знаний, как у меня, когда я разместил этот вопрос, и кто желает узнать ту же информацию, вот мое решение вопроса. Краткий ответ на быстрый вопрос «можно ли увеличить переменную». Нет! Но вы можете установить переменную и переместить позицию с помощью следующего фрагмента:

<xsl:for-each select="/dataset/rtbp">
   <xsl:variable name="i" select="position()" />
 </xsl:for-each> 

Этот фрагмент просматривает таблицы rtbp в исходном XML и перемещает позицию на одну позицию больше в каждом цикле. Это создает объект, который можно использовать внутри XPath для проверки состояния каждого появления Xpath с одним и тем же путем URI. Такой как:

<xsl:for-each select="/dataset/rtbp">
    <xsl:variable name="i" select="position()" />
    <xsl:if test="/dataset/FunctionSet[$i]/cfmid = /dataset/rtbp[$n]/cfmid">
       <!--code if condition is true-->
 </xsl:for-each> 

[$variable name] - это то, как вы направляете XPath на вхождение имени элемента. Поэтому, когда i = 1, он ищет первое вхождение имени элемента в XPath, а затем, когда i = 2, он ищет второе вхождение имени элемента в XPath.

Функция Key - хороший инструмент для поиска ключевого условия внутри шаблона. Однако я могу использовать только одну ключевую функцию в шаблоне. если вы хотите иметь тест с несколькими условиями, вы должны использовать оператор select when с несколькими операторами if, дополняемыми каждым другим. Например:

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

С помощью функции позиции и записи предиката XPath в сочетании с операторами select when с помощью ands вы можете создать сложный XSL, который может воссоздать плоский список XML-таблиц базы данных в иерархическую XML-форму.

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

-1
user3635688 22 Май 2014 в 14:35

Цель состоит в том, чтобы воссоздать иерархический XML из плоского XML, экспортированного из базы данных с помощью DBunit. Связь может быть выполнена с помощью cmfid

Вам обязательно следует использовать ключ на основе соответствия значению cfmid, особенно если вы ожидаете большое количество строк. Пытаться:

XSLT 1.0

<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:key name="func" match="FunctionSet" use="RTBP__CFMID" />

<xsl:template match="/">
    <root>
        <xsl:for-each select="dataset/rtbp">
            <RTBP CFMtype="{cfmtype}" CFMid="{cfmid}">
                <xsl:for-each select="key('func', cfmid)">
                    <FunctionSet RTBP__CFMid="{RTBP__CFMID}" RTBP_FunctionSet="{FUNCTIONSET__IDENTIFIER}"/>
                </xsl:for-each>
            </RTBP>
        </xsl:for-each>
    </root>
</xsl:template>

</xsl:stylesheet>

Когда вышеуказанное применяется к следующему тесту :

<?xml version="1.0"?>
<dataset>
 <rtbp>
  <cfmtype>dog</cfmtype>
  <cfmid>124</cfmid>
 </rtbp>
 <rtbp>
  <cfmtype>cat</cfmtype>
  <cfmid>256</cfmid>
 </rtbp>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>Canine</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>124</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>Feline</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>256</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>Hound</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>124</RTBP__CFMID>
 </FunctionSet>
</dataset>

результат :

<?xml version="1.0" encoding="utf-8"?>
<root>
  <RTBP CFMtype="dog" CFMid="124">
    <FunctionSet RTBP__CFMid="124" RTBP_FunctionSet="Canine"/>
    <FunctionSet RTBP__CFMid="124" RTBP_FunctionSet="Hound"/>
  </RTBP>
  <RTBP CFMtype="cat" CFMid="256">
    <FunctionSet RTBP__CFMid="256" RTBP_FunctionSet="Feline"/>
  </RTBP>
</root>

Обратите внимание, что запрошенный вами выходной формат без необходимости дублирует значение cfmid как в родительском, так и в дочернем.

2
michael.hor257k 14 Май 2014 в 11:44

Я думаю, вы ищете что-то вроде (обновлено после обновления очереди):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
        <xsl:output indent="yes"/>
        <xsl:template match="node()|@*">
            <xsl:copy>
                <xsl:apply-templates select="node()|@*"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="rtbp">
            <xsl:copy>
                <xsl:for-each select="*">
                    <xsl:attribute name="{local-name()}" select="."/>
                </xsl:for-each>
                <xsl:apply-templates 
                             select="//FunctionSet[RTBP__CFMID = current()/cfmid]"
                     mode="insertFunctionSet"/>
            </xsl:copy>
        </xsl:template>
        <xsl:template match="FunctionSet"/>
        <xsl:template match="FunctionSet" mode="insertFunctionSet">
            <xsl:copy>
                <xsl:for-each select="*">
                    <xsl:attribute name="{local-name()}" select="."/>
                </xsl:for-each>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>

Идея здесь состоит в том, чтобы по-другому обрабатывать элемент FunctionSet в контексте элемента rtbp.

Он не должен быть частью вывода, когда вы рекурсивно перебираете все дерево (это цель <xsl:template match="FunctionSet"/>).

Но это должно обрабатываться внутри элемента rtbp, поэтому на этом этапе мы применяем шаблоны к соответствующему FunctionSet в определенном режиме. Это цель <xsl:template match="FunctionSet" mode="insertFunctionSet">...</xsl:template>

С вашим участием:

<?xml version="1.0"?>
<dataset>
 <rtbp>
  <cfmtype>dog</cfmtype>
  <cfmid>1</cfmid>
 </rtbp>
 <rtbp>
  <cfmtype>cat</cfmtype>
  <cfmid>2</cfmid>
 </rtbp>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>1</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>1</RTBP__CFMID>
 </FunctionSet>
 <FunctionSet> 
  <FUNCTIONSET__IDENTIFIER>2</FUNCTIONSET__IDENTIFIER>
  <RTBP__CFMID>2</RTBP__CFMID>
 </FunctionSet>
</dataset>

Результат:

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
   <rtbp cfmtype="dog" cfmid="1">
      <FunctionSet FUNCTIONSET__IDENTIFIER="1" RTBP__CFMID="1"/>
   </rtbp>
   <rtbp cfmtype="cat" cfmid="2">
      <FunctionSet FUNCTIONSET__IDENTIFIER="2" RTBP__CFMID="2"/>
   </rtbp>
</dataset>
1
Vincent Biragnet 14 Май 2014 в 11:39