Объединить записи в XML

0

У меня есть продукты, содержащие XML, и мне нужно как-то слиться с одной записью:

<SHOPITEM>
        <PRODUCT>POINT</PRODUCT>
        <FRAMESIZE>MD</FRAMESIZE>
        <CODE>029,00</CODE>
        <COLOR>black / yellow</COLOR>
</SHOPITEM>
<SHOPITEM>
        <PRODUCT>POINT</PRODUCT>
        <FRAMESIZE>LD</FRAMESIZE>
        <CODE>029,01</CODE>
        <COLOR>black / yellow</COLOR>
</SHOPITEM>
<SHOPITEM>
        <PRODUCT>POINT</PRODUCT>
        <FRAMESIZE>LD</FRAMESIZE>
        <CODE>029,03</CODE>
        <COLOR>green / white</COLOR>
</SHOPITEM>
<SHOPITEM>
        <PRODUCT>POINT</PRODUCT>
        <FRAMESIZE>MD</FRAMESIZE>
        <CODE>029,04</CODE>
        <COLOR>green / white</COLOR>
</SHOPITEM>

<PRODUCT> - это то же самое, что и есть изменение <FRAMESIZE>, <CODE>, <COLOR>.

Есть ли способ получить от этого полезную информацию? Лучше всего было бы в PHP, но также было бы полезно создать новый XML файл, который я могу обработать на PHP:

<SHOPITEM>
        <PRODUCT>POINT</PRODUCT>
        <FRAMESIZE1>MD</FRAMESIZE1>
        <CODE1>029,00</CODE1>
        <COLOR1>black / yellow</COLOR2>
        <FRAMESIZE2>LD</FRAMESIZE2>
        <CODE2>029,01</CODE2>
        <COLOR2>black / yellow</COLOR2>
        <FRAMESIZE3>LD</FRAMESIZE3>
        <CODE3>029,03</CODE3>
        <COLOR3>green / white</COLOR3>
        <FRAMESIZE4>MD</FRAMESIZE4>
        <CODE4>029,04</CODE4>
        <COLOR4>green / white</COLOR4>
</SHOPITEM>
Теги:
xslt

3 ответа

2
Лучший ответ

Я настоятельно рекомендую вам разобраться в решении XSLT - glenn jackman

Я могу только это повторить. Итак, вот ваше решение XSLT. Однако возникает вопрос: вы показывали репрезентативный образец XML или есть несколько различных элементов PRODUCT в ваших реальных данных XML?

Кроме того, можно называть элементы CODE1, CODE2 и т.д., Но я (решительно) рекомендую не делать этого. Я рад добавить эту деталь, но сначала уточню, действительно ли вам нужно это соглашение об уничтожении вредоносных или если вы можете использовать атрибуты вместо этого:

<CODE n="1"/>

Вход XML

Как уже было предложено Гленном, должен быть один внешний элемент, чтобы сделать ваш вход хорошо сформированным XML.

<root>
    <SHOPITEM>
            <PRODUCT>POINT</PRODUCT>
            <FRAMESIZE>MD</FRAMESIZE>
            <CODE>029,00</CODE>
            <COLOR>black / yellow</COLOR>
    </SHOPITEM>
    <SHOPITEM>
            <PRODUCT>POINT</PRODUCT>
            <FRAMESIZE>LD</FRAMESIZE>
            <CODE>029,01</CODE>
            <COLOR>black / yellow</COLOR>
    </SHOPITEM>
    <SHOPITEM>
            <PRODUCT>POINT</PRODUCT>
            <FRAMESIZE>LD</FRAMESIZE>
            <CODE>029,03</CODE>
            <COLOR>green / white</COLOR>
    </SHOPITEM>
    <SHOPITEM>
            <PRODUCT>POINT</PRODUCT>
            <FRAMESIZE>MD</FRAMESIZE>
            <CODE>029,04</CODE>
            <COLOR>green / white</COLOR>
    </SHOPITEM>
</root>

XSLT Stylesheet (1.0)

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

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

    <xsl:template match="/root">
        <SHOPITEM>
            <xsl:copy-of select="SHOPITEM[1]/PRODUCT"/>
            <xsl:copy-of select="SHOPITEM/*[not(self::PRODUCT)]"/>
        </SHOPITEM>
    </xsl:template>

</xsl:transform>

Выход XML

<SHOPITEM>
   <PRODUCT>POINT</PRODUCT>
   <FRAMESIZE>MD</FRAMESIZE>
   <CODE>029,00</CODE>
   <COLOR>black / yellow</COLOR>
   <FRAMESIZE>LD</FRAMESIZE>
   <CODE>029,01</CODE>
   <COLOR>black / yellow</COLOR>
   <FRAMESIZE>LD</FRAMESIZE>
   <CODE>029,03</CODE>
   <COLOR>green / white</COLOR>
   <FRAMESIZE>MD</FRAMESIZE>
   <CODE>029,04</CODE>
   <COLOR>green / white</COLOR>
</SHOPITEM>

EDIT:

То, что я тоже пропустил, что есть много разных элементов, как спросил Матиас.

Вход XML

Более разумный образец для тестирования с более чем одним PRODUCT:

<root>
    <SHOPITEM>
            <PRODUCT>POINT</PRODUCT>
            <FRAMESIZE>MD</FRAMESIZE>
            <CODE>029,00</CODE>
            <COLOR>black / yellow</COLOR>
    </SHOPITEM>
    <SHOPITEM>
            <PRODUCT>POINT</PRODUCT>
            <FRAMESIZE>LD</FRAMESIZE>
            <CODE>029,01</CODE>
            <COLOR>black / yellow</COLOR>
    </SHOPITEM>
    <SHOPITEM>
            <PRODUCT>OTHER</PRODUCT>
            <FRAMESIZE>LD</FRAMESIZE>
            <CODE>029,03</CODE>
            <COLOR>green / white</COLOR>
    </SHOPITEM>
    <SHOPITEM>
            <PRODUCT>OTHER</PRODUCT>
            <FRAMESIZE>MD</FRAMESIZE>
            <CODE>029,04</CODE>
            <COLOR>green / white</COLOR>
    </SHOPITEM>
</root>

стилей

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />

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

    <xsl:key name="prod" match="SHOPITEM" use="PRODUCT"/>

    <xsl:template match="/root">
        <xsl:copy>
            <xsl:for-each select="SHOPITEM[generate-id() = generate-id(key('prod',PRODUCT)[1])]">
                <SHOPITEM>
                    <xsl:copy-of select="PRODUCT"/>
                    <xsl:copy-of select="/root/SHOPITEM[PRODUCT = current()/PRODUCT]/*[not(self::PRODUCT)]"/>
                </SHOPITEM>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

</xsl:transform>

Выход XML

<root>
   <SHOPITEM>
      <PRODUCT>POINT</PRODUCT>
      <FRAMESIZE>MD</FRAMESIZE>
      <CODE>029,00</CODE>
      <COLOR>black / yellow</COLOR>
      <FRAMESIZE>LD</FRAMESIZE>
      <CODE>029,01</CODE>
      <COLOR>black / yellow</COLOR>
   </SHOPITEM>
   <SHOPITEM>
      <PRODUCT>OTHER</PRODUCT>
      <FRAMESIZE>LD</FRAMESIZE>
      <CODE>029,03</CODE>
      <COLOR>green / white</COLOR>
      <FRAMESIZE>MD</FRAMESIZE>
      <CODE>029,04</CODE>
      <COLOR>green / white</COLOR>
   </SHOPITEM>
</root>
  • 0
    Я надеюсь, что вы видите это. :) Этот метод XLST выглядит очень хорошо, но есть ли способ сделать вывод, как это? bit.ly/1E5maUp (использовать атрибуты на элементах варианта продукта)
  • 0
    @Adrian Выходной формат - это то, что вы сами разработали? Я бы посоветовал против этого, потому что это вводит в заблуждение, что нет @id = 1 . В любом случае, вы никогда не должны менять свой вопрос, ссылаясь где-то за пределами сайта. Либо отредактируйте свой вопрос, чтобы отразить то, что вы действительно хотели задать. Или примите один из ответов здесь и опубликуйте другой вопрос, в котором вы включите настоящий XML, потому что нам действительно нужно увидеть реальную проблему и использовать код отсюда в начальной точке. На мой взгляд, вы должны пойти на вариант 2, потому что этот пост уже слишком длинный.
Показать ещё 1 комментарий
2

Мой XSLT-fu слаб, но это дает желаемый результат (после упаковки вашего образца XML с помощью корневого тега):

xmlstarlet sel -t -v '//SHOPITEM[1]/PRODUCT' -n -m '//SHOPITEM' -v FRAMESIZE -n -v CODE -n -v COLOR -n file.xml | 
awk '
  BEGIN {print "<SHOPITEM>"} 
  END   {print "</SHOPITEM>"}
  NR==1 {print "  <PRODUCT>" $0 "</PRODUCT>"; next} 
  {
    n++;     t="FRAMESIZE"; printf "  <%s%d>%s</%s%d>\n", t, n, $0, t, n
    getline; t="CODE";      printf "  <%s%d>%s</%s%d>\n", t, n, $0, t, n
    getline; t="COLOR";     printf "  <%s%d>%s</%s%d>\n", t, n, $0, t, n
  }
'
<SHOPITEM>
  <PRODUCT>POINT</PRODUCT>
  <FRAMESIZE1>MD</FRAMESIZE1>
  <CODE1>029,00</CODE1>
  <COLOR1>black / yellow</COLOR1>
  <FRAMESIZE2>LD</FRAMESIZE2>
  <CODE2>029,01</CODE2>
  <COLOR2>black / yellow</COLOR2>
  <FRAMESIZE3>LD</FRAMESIZE3>
  <CODE3>029,03</CODE3>
  <COLOR3>green / white</COLOR3>
  <FRAMESIZE4>MD</FRAMESIZE4>
  <CODE4>029,04</CODE4>
  <COLOR4>green / white</COLOR4>
</SHOPITEM>

Оглядываясь назад, этот формат вывода может быть проще обрабатывать:

xmlstarlet ... file.xml | awk '
      BEGIN {print "<SHOPITEM>"; fmt="\t\t<%s>%s</%s>\n"} 
      END   {print "</SHOPITEM>"}
      NR==1 {print "\t<PRODUCT>" $0 "</PRODUCT>"; next} 
      {
        n++
        printf "\t<PRODUCT_ITEM id=\"%d\">\n", n
        t="FRAMESIZE"; printf fmt, t, $0, t; getline
        t="CODE";      printf fmt, t, $0, t; getline
        t="COLOR";     printf fmt, t, $0, t
        print "\t</PRODUCT_ITEM>"
      }
    '
<SHOPITEM>
    <PRODUCT>POINT</PRODUCT>
    <PRODUCT_ITEM id="1">
        <FRAMESIZE>MD</FRAMESIZE>
        <CODE>029,00</CODE>
        <COLOR>black / yellow</COLOR>
    </PRODUCT_ITEM>
    <PRODUCT_ITEM id="2">
        <FRAMESIZE>LD</FRAMESIZE>
        <CODE>029,01</CODE>
        <COLOR>black / yellow</COLOR>
    </PRODUCT_ITEM>
    <PRODUCT_ITEM id="3">
        <FRAMESIZE>LD</FRAMESIZE>
        <CODE>029,03</CODE>
        <COLOR>green / white</COLOR>
    </PRODUCT_ITEM>
    <PRODUCT_ITEM id="4">
        <FRAMESIZE>MD</FRAMESIZE>
        <CODE>029,04</CODE>
        <COLOR>green / white</COLOR>
    </PRODUCT_ITEM>
</SHOPITEM>
  • 0
    Привет, Гленн, это awk-решение выглядит фантастически. Я пропустил некоторые теги из вопроса, я попытался добавить отсутствующие теги в команду, но безуспешно: bit.ly/1xtJXHR , вот новая команда с тегами, которые я пропустил: bit.ly/1sDKH1g
  • 0
    Это решение абсолютно полагается на xmlstarlet для анализа XML. Я настоятельно рекомендую вам найти решение XSLT
Показать ещё 3 комментария
1

Вот еще одно решение в XSLT 1.0, которое предполагает, что может быть несколько элементов <SHOPTITEM>.

Я добавил корневой элемент (<root>), потому что ваш входной XML не был корректным. Вы также можете увидеть/протестировать решение здесь: http://xsltransform.net/pPqsHTk

Обратите внимание, что существует один шаблон, соответствующий первому PRODUCT, который группирует данные в соответствии с названием PRODUCT. И еще один шаблон, который обрабатывает все вхождения одного и того же ПРОДУКТА, которые не являются первыми и просто ничего не делают.

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

    <xsl:template match="root">
        <xsl:copy>
            <xsl:apply-templates />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="SHOPITEM[not(PRODUCT = preceding::SHOPITEM/PRODUCT)]">
        <SHOPITEM>
            <xsl:copy-of select="*"/>
            <xsl:copy-of select="following-sibling::SHOPITEM[PRODUCT = current()/PRODUCT]/*[not(self::PRODUCT)]"/>
        </SHOPITEM>
    </xsl:template>

    <xsl:template match="SHOPITEM[PRODUCT = preceding::SHOPITEM/PRODUCT]"/>
</xsl:transform>

Это не самое быстрое решение, но оно должно работать достаточно быстро, если ваш XML-вход не слишком большой.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню