2015-01-15 2 views
0

У меня есть XML, содержащий продукты, и мне нужно каким-то образом объединить в одну запись:записей Объединить в 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> 
+0

Я не знаком с этой темой. Возможно, это поможет вам: https://en.wikipedia.org/wiki/XSLT и http://php.net/manual/en/class.xsltprocessor.php – Cyrus

ответ

2

Я сильно рекомендуем вам выяснить в XSLT решение - Glenn Джекман

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

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

<CODE n="1"/> 

XML Input

Как предложил Гленн уже, должен быть один внешний элемент, чтобы сделать ваш вход хорошо сформированный 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 (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> 

РЕДАКТИРОВАТЬ:

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

XML Input

Более разумный образец для тестирования, с более чем одним 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> 

Stylesheet

<?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 Output

<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 выглядит очень красиво, но есть ли способ сделать такой вывод? http://bit.ly/1E5maUp (использовать атрибуты для элементов продукта) – Adrian

+0

@Adrian Является ли формат вывода тем, что вы сами разработали? Я бы посоветовал это, потому что кажется неправильным, что нет '@id = 1'. В любом случае, вы никогда не должны менять свой вопрос, связываясь где-то вне сайта. Либо _edit_ ваш вопрос, чтобы отразить то, что вы _really_ хотели спросить. Или, примите один из ответов здесь, и задайте другой вопрос, в который вы включаете _actual_ XML, потому что нам действительно нужно увидеть проблему _real_ и использовать код здесь в начальной точке. На мой взгляд, вы должны пойти на вариант 2, потому что этот пост уже слишком длинный. –

+0

Вы правы, я открыл новый вопрос и принял ваше решение, потому что он универсален (XLST). – Adrian

2

Мой XSLT-фу является слабым, но это производит ваш желаемый результат (после того, как упаковка вашего образца 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-решение выглядит фантастическим. Я пропустил некоторые теги из вопроса, я попытался добавить отсутствующие теги в команду без успеха: http://bit.ly/1xtJXHR, вот новая команда с тегами, которые я пропустил: http://bit.ly/1sDKH1g – Adrian

+0

Это решение абсолютно зависит от 'xmlstarlet' для анализа XML. Я ** сильно ** рекомендую вам разобраться с решением XSLT –

+0

Я бы тоже так сказал - XSLT - правильный инструмент, на мой взгляд. Но +1 во всяком случае, я бы не подумал, что это возможно в оболочке. Спасибо за retag, кстати! –

1

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

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

Обратите внимание, что существует один шаблон, соответствующий первому 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 не слишком велик.