У меня есть продукты, содержащие 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 - 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>
@id = 1
. В любом случае, вы никогда не должны менять свой вопрос, ссылаясь где-то за пределами сайта. Либо отредактируйте свой вопрос, чтобы отразить то, что вы действительно хотели задать. Или примите один из ответов здесь и опубликуйте другой вопрос, в котором вы включите настоящий XML, потому что нам действительно нужно увидеть реальную проблему и использовать код отсюда в начальной точке. На мой взгляд, вы должны пойти на вариант 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>
xmlstarlet
для анализа XML. Я настоятельно рекомендую вам найти решение XSLT
Вот еще одно решение в 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-вход не слишком большой.