XSLT трансформирует XML - PullRequest
       1

XSLT трансформирует XML

1 голос
/ 17 ноября 2011

Я новичок в xsl. Я хочу преобразовать XML из,

<result name="response" numFound="1" start="0">
  <doc>
    <str name="q">what</str>
    <arr name="suggestion">
                <str>what1</str>
                <str>what2</str>
   </arr>
  </doc>
</result>

до

<result name="response" numFound="2" start="0">
  <doc>
    <str name="q">what</str>
    <str name="suggestion">what1</str>
  </doc>
  <doc>
    <str name="q">what</str>
    <str name="suggestion">what2</str>
  </doc>
</result>

Я мог бы извлечь тексты "what1" и "what2", используя

<xsl:template match="/response/result[@name='response']">
             <xsl:for-each select="./doc/arr[@name='suggestion']/str">
                    <xsl:value-of select="normalize-space(.)"/>
                    <xsl:value-of select="$endl"/>
             </xsl:for-each>
    </xsl:template>

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

Дополнительная функция:

Может кто-нибудь сказать, могу ли я добавить в выходной документ поле с именем <float name="score">, которое будет увеличиваться на 100 с каждым документом?

например) выход:

   <response>
      <lst name="responseHeader">
        <int name="status">0</int>
        <int name="QTime">1</int>
        <lst name="params">
          <str name="indent">on</str>
          <str name="q">"what"</str>
        </lst>
      </lst>
      <result numFound="2" name="response" start="0">
        <doc>
          <str name="query">what</str>
          <str>what1</str>
          <float name="score">100</float>
        </doc>
        <doc>
          <str name="query">what</str>
          <str>what2</str>
          <float name="score">200</float>
        </doc>
        <doc>
          <str name="query">what</str>
          <str>what3</str>
          <float name="score">300</float>
        </doc>
      </result>
    </response>

Подскажите, пожалуйста, какую функцию я предполагаю использовать?

Ответы [ 2 ]

3 голосов
/ 17 ноября 2011

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

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="/*">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:attribute name="numFound"><xsl:value-of select="count(doc/arr[@name='suggestion']/str)" /></xsl:attribute>
            <xsl:apply-templates select="doc/arr[@name='suggestion']" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="arr[@name='suggestion']">
        <xsl:for-each select="str">
            <doc>
                <xsl:copy-of select="parent::node()/preceding-sibling:: str[@name='q']" />
                <xsl:variable name="nameAttributeValue" select="parent::node()/@name" />
                <xsl:element name="str"><xsl:attribute name="name"><xsl:value-of select="$nameAttributeValue" /></xsl:attribute><xsl:value-of select="." /></xsl:element>
            </doc>
        </xsl:for-each>
    </xsl:template> 

</xsl:stylesheet>

РЕДАКТИРОВАТЬ: В ответ на ваш комментарий: обратите внимание, как мой первый шаблон соответствует на /*. Это сводится к «любому корневому элементу». Если выражение XPath начинается с одного /, оно всегда относительно корня документа. Если он начинается с двойного //, это в основном означает где-нибудь в текущем узле. Если он не начинается с косой черты, он относится к текущему узлу.

Чтобы обработать XML, как в вашем комментарии, вам нужно изменить таблицу стилей на ...

<?xml version="1.0" encoding="UTF-8"?>
<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:template match="node() | @*">
        <xsl:apply-templates select="node() | @*" />
    </xsl:template>

    <xsl:template match="/response/result">
        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:attribute name="numFound"><xsl:value-of select="count(doc/arr[@name='suggestion']/str)" /></xsl:attribute>
            <xsl:apply-templates select="doc/arr[@name='suggestion']" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="arr[@name='suggestion']">
        <xsl:for-each select="str">
            <doc>
                <xsl:copy-of select="parent::node()/preceding-sibling:: str[@name='q']" />
                <xsl:variable name="nameAttributeValue" select="parent::node()/@name" />
                <xsl:element name="str"><xsl:attribute name="name"><xsl:value-of select="$nameAttributeValue" /></xsl:attribute><xsl:value-of select="." /></xsl:element>
            </doc>
        </xsl:for-each>
    </xsl:template> 

</xsl:stylesheet>

Этот первый шаблон, который я добавил, должен гарантировать, что мы переопределим какой-то шаблон по умолчанию, скрытый в XSLT. Имейте в виду, что это обеспечит правильный вывод, только если в response.

есть только один элемент result.

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

1 голос
/ 17 ноября 2011

Вот решение для комментария ОП, который обновляет вопрос:

Это преобразование :

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

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

 <xsl:template match="result">
  <result numFound="{count(doc/arr/str)}">
   <xsl:apply-templates select="@*[not(name()='numFound')]"/>
   <xsl:apply-templates/>
  </result>
 </xsl:template>

 <xsl:template match="arr[@name='suggestion']/str">
  <doc>
    <xsl:copy-of select="../../str"/>
    <xsl:copy-of select="."/>
  </doc>
 </xsl:template>

 <xsl:template match="doc|doc/arr">
  <xsl:apply-templates/>
 </xsl:template>
 <xsl:template match="doc/str "/>
</xsl:stylesheet>

при применении к следующему (при условиив комментарии ОП) XML-документ :

<response>
    <lst name="responseHeader">
        <int name="status">0</int>
        <int name="QTime">1</int>
        <lst name="params">
            <str name="indent">on</str>
            <str name="q">"what"</str>
        </lst>
    </lst>
    <result name="response" numFound="1" start="0">
        <doc>
            <str name="q">what</str>
            <arr name="suggestion">
                <str>what1</str>
                <str>what2</str>
            </arr>
        </doc>
    </result>
</response>

дает требуемый, правильный результат :

<response>
   <lst name="responseHeader">
      <int name="status">0</int>
      <int name="QTime">1</int>
      <lst name="params">
         <str name="indent">on</str>
         <str name="q">"what"</str>
      </lst>
   </lst>
   <result numFound="2" name="response" start="0">
      <doc>
         <str name="q">what</str>
         <str>what1</str>
      </doc>
      <doc>
         <str name="q">what</str>
         <str>what2</str>
      </doc>
   </result>
</response>

Обновление :

В обновлении своего вопроса ОП запросил:

Может кто-нибудь сказать, могу ли я добавить в выходной документ поле с именем <float name="score">, которое будет увеличено на 100с каждым документом?

Этого чрезвычайно легко достичь .Мы добавляем всего три строки кода к существующему преобразованию:

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

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

 <xsl:template match="result">
    <result numFound="{count(doc/arr/str)}">
        <xsl:apply-templates select="@*[not(name()='numFound')]"/>
        <xsl:apply-templates/>
    </result>
 </xsl:template>

 <xsl:template match="arr[@name='suggestion']/str">
    <doc>
        <xsl:copy-of select="../../str"/>
        <xsl:copy-of select="."/>
        <float name="score">
         <xsl:value-of select="100*position()"/>
        </float>    
    </doc>
 </xsl:template>

 <xsl:template match="doc|doc/arr">
    <xsl:apply-templates/>
 </xsl:template>
 <xsl:template match="doc/str "/>
</xsl:stylesheet>

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

<response>
    <lst name="responseHeader">
        <int name="status">0</int>
        <int name="QTime">1</int>
        <lst name="params">
            <str name="indent">on</str>
            <str name="q">"what"</str>
        </lst>
    </lst>
    <result name="response" numFound="1" start="0">
        <doc>
            <str name="q">what</str>
            <arr name="suggestion">
                <str>what1</str>
                <str>what2</str>
                <str>what3</str>
            </arr>
        </doc>
    </result>
</response>

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

<response>
   <lst name="responseHeader">
      <int name="status">0</int>
      <int name="QTime">1</int>
      <lst name="params">
         <str name="indent">on</str>
         <str name="q">"what"</str>
      </lst>
   </lst>
   <result numFound="3" name="response" start="0">
      <doc>
         <str name="q">what</str>
         <str>what1</str>
         <float name="score">100</float>
      </doc>
      <doc>
         <str name="q">what</str>
         <str>what2</str>
         <float name="score">200</float>
      </doc>
      <doc>
         <str name="q">what</str>
         <str>what3</str>
         <float name="score">300</float>
      </doc>
   </result>
</response>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...