Преобразование одного формата XML в другой формат XML с помощью XSL

#xml #xslt #spring-mvc

#xml #xslt #spring-mvc

Вопрос:

У меня есть код с выводом XML в виде

 <?xml version="1.0" encoding="windows-1252" standalone="yes"?>
<products>
    <product>
        <name>abc</name>
        <id>1</id>
    </product>
    <product>
        <name>klm</name>
        <id>2</id>
    </product>
</products>
  

Я хочу, чтобы тот же XML отображался и в следующем формате:

 <?xml version="1.0" encoding="windows-1252" standalone="yes"?>
<products>
    <product>
        <name>
            <value>abc</value>
            <unit></unit>
        </name>
        <id>
            <value>1</value>
            <unit></unit>
        </id>
        <product>
        <name>
            <value>klm</value>
            <unit></unit>
        </name>
        <id>
            <value>2</value>
            <unit></unit>
        </id>
</product>
  

Как я могу это сделать с помощью XSLT?

Я использую среду Spring. XML-теги для продукта являются переменными. Они различаются в зависимости от типа продуктов. Код для генерации XML является

 JAXBContext jc;
try {
   jc = JAXBContext.newInstance(cla);
   Marshaller m;
   m = jc.createMarshaller();
   m.marshal(obj, out); 
} catch (JAXBException e) {
   e.printStackTrace();
}
  

Редактировать:
продукт может иметь атрибут weight, поэтому также будет тег

 <weight>10lbs<weight>
  

Этот параметр будет разбит на

 <weight>
    <value>10</value>
    <unit>lbs</unit>
</weight> 
  

Комментарии:

1. Какие данные входят в <unit /> тег?

2. Имя равно нулю, но имеет значение, если тегом является weight или speed и т.д.

3. Можете ли вы также указать этот случай в своем примере кода?

Ответ №1:

Вот пример таблицы стилей [отредактировано, чтобы отразить новое требование с элементом weight]:

 <xsl:stylesheet
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">

 <xsl:strip-space elements="*"/>
 <xsl:output indent="yes"/>

 <xsl:template match="products">
   <xsl:copy>
     <product>
       <xsl:apply-templates select="product/*"/>
     </product>
   </xsl:copy>
 </xsl:template>

 <xsl:template match="product/*">
   <xsl:copy>
     <value>
       <xsl:value-of select="."/>
     </value>
     <unit></unit>
   </xsl:copy>
 </xsl:template>

 <xsl:template match="product/weight" priority="3">
   <xsl:copy>
     <value>
       <xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', '')"/>
     </value>
     <unit>
       <xsl:value-of select="translate(., '0123456789', '')"/>
     </unit>
   </xsl:copy>
 </xsl:template>

</xsl:stylesheet>
  

Когда я применяю это к образцу входных данных

 <products>
    <product>
        <name>abc</name>
        <id>1</id>
    </product>
    <product>
        <name>klm</name>
        <id>2</id>
    </product>
    <product>
        <name>foo</name>
        <id>3</id>
        <weight>10lbs</weight>
     </product>
</products>
  

Я получаю

 <products>
   <product>
      <name>
         <value>abc</value>
         <unit/>
      </name>
      <id>
         <value>1</value>
         <unit/>
      </id>
      <name>
         <value>klm</value>
         <unit/>
      </name>
      <id>
         <value>2</value>
         <unit/>
      </id>
      <name>
         <value>foo</value>
         <unit/>
      </name>
      <id>
         <value>3</value>
         <unit/>
      </id>
      <weight>
         <value>10</value>
         <unit>lbs</unit>
      </weight>
   </product>
</products>
  

Основываясь на комментарии Алехандро, я, возможно, неправильно истолковал требуемый результат, и вы не хотите объединять продукты, в этом случае используйте

 <xsl:stylesheet
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 version="1.0">

 <xsl:strip-space elements="*"/>
 <xsl:output indent="yes"/>

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

 <xsl:template match="product/*">
   <xsl:copy>
     <value>
       <xsl:value-of select="."/>
     </value>
     <unit></unit>
   </xsl:copy>
 </xsl:template>

 <xsl:template match="product/weight" priority="3">
   <xsl:copy>
     <value>
       <xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', '')"/>
     </value>
     <unit>
       <xsl:value-of select="translate(., '0123456789', '')"/>
     </unit>
   </xsl:copy>
 </xsl:template>

</xsl:stylesheet>
  

затем вы получаете

 <products>
   <product>
      <name>
         <value>abc</value>
         <unit/>
      </name>
      <id>
         <value>1</value>
         <unit/>
      </id>
   </product>
   <product>
      <name>
         <value>klm</value>
         <unit/>
      </name>
      <id>
         <value>2</value>
         <unit/>
      </id>
   </product>
   <product>
      <name>
         <value>foo</value>
         <unit/>
      </name>
      <id>
         <value>3</value>
         <unit/>
      </id>
      <weight>
         <value>10</value>
         <unit>lbs</unit>
      </weight>
   </product>
</products>
  

Комментарии:

1. @Martin Honnen Когда я применяю эту таблицу стилей, все XML-теги теряются, и результатом является xbc 1 klm 2.

2. Фарьял, ты уверен, что входные данные в точности соответствуют опубликованным тобой? Звучит так, как будто вы используете совершенно другой входной документ с опубликованными таблицами стилей, возможно, с элементами в каком-то пространстве имен, которое вы нам не показали.

3. Это именно тот результат, который генерируется кодом:<?xml version=»1.0″ encoding = «UTF-8″ standalone =»yes» ?> <продукт> <категория> Ноутбук</category> <описание>Lenovo ThinkPad X100e 2876 — Athlon Neo MV-40 / 1.6 ГГц — Оперативная память 3 ГБ — Жесткий диск 250 ГБ — Мобильность Radeon HD 3200 — Мобильный широкополосный доступ 3G — Гигабитный Ethernet — WLAN: 802.11b / g / n Bluetooth 2.1 — TPM — Windows 7 Pro — 11.6 » Широкоэкранный TFT 1366 x 768 ( WXGA) — камера</description> <id> 1</id> <язык> Английский</language> <вес> 0 фунтов</weight> <беспроводной>WLAN: 802.11b/ g / n Bluetooth 2.1</беспроводной> </продукт>

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

5. @Martin Honnen: Я думаю, что нет необходимости в объединении product , это просто ошибка желаемого отступа на выходе.

Ответ №2:

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
  <xsl:template match="/products">
  <xsl:for-each select="product">
   <xsl:element name="product">
     <xsl:element name="name">
       <xsl:element name="value"><xsl:value-of select="name"/></xsl:element>
       <xsl:element name="unit"/>
     </xsl:element>
     <xsl:element name="id">
       <xsl:element name="value"><xsl:value-of select="id"/></xsl:element>
       <xsl:element name="unit"/>
      </xsl:element>
   </xsl:element>
  </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
  

Комментарии:

1. Вы не должны использовать <xsl:for-each>

2. @conqenator Когда я применяю эту таблицу стилей, все XML-теги теряются, а выводом является xbc 1 klm 2

3. @Faryal: Извините, я не могу протестировать код прямо сейчас. Теперь я это исправил. Смотрите, как я редактирую.

4. @Faryal: вы либо неправильно запускаете таблицу стилей, либо неправильно просматриваете выходные данные.

5. @TOUDIdel «Вам не следует использовать <xsl:for-each> «. Это комментарий от кого-то с небольшими знаниями. Небольшие знания — опасная вещь. Инструкция xsl:for-each предназначена для определенной цели. Его использование здесь вполне оправданно. Если бы я должен был критиковать этот код, мой первый комментарий был бы об использовании xsl:element вместо буквальных элементов результата, что является совершенно излишне подробным.