合并 XML 中的条目
Merge entries in 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>
我的 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>" [=10=] "</PRODUCT>"; next}
{
n++; t="FRAMESIZE"; printf " <%s%d>%s</%s%d>\n", t, n, [=10=], t, n
getline; t="CODE"; printf " <%s%d>%s</%s%d>\n", t, n, [=10=], t, n
getline; t="COLOR"; printf " <%s%d>%s</%s%d>\n", t, n, [=10=], 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>" [=12=] "</PRODUCT>"; next}
{
n++
printf "\t<PRODUCT_ITEM id=\"%d\">\n", n
t="FRAMESIZE"; printf fmt, t, [=12=], t; getline
t="CODE"; printf fmt, t, [=12=], t; getline
t="COLOR"; printf fmt, t, [=12=], 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>
I strongly recommend you figure out an XSLT solution - glenn jackman
我只能支持。所以,这是您的 XSLT 解决方案。但是,问题是:您是否展示了 代表 XML 示例,或者您的 真实 中是否有几个不同的 PRODUCT
元素] XML数据?
此外,命名元素 CODE1
、CODE2
等也可以,但我(再次强烈)建议不要这样做。我很高兴添加这个细节,但首先要澄清您是否 真的 需要这种严重的命名约定,或者您是否可以改用属性:
<CODE n="1"/>
XML 输入
正如 Glenn 已经建议的那样,必须有一个最外层元素才能使您的输入格式正确 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>
编辑:
What I missed too, that there is many different elements as Mathias asked.
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>
这是 XSLT 1.0 中的另一个解决方案 - 它假设可以有多个 <SHOPTITEM>
元素。
我添加了一个根元素 (<root>
),因为您的输入 XML 格式不正确。您也可以在此处 see/test 解决方案: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 不是太大,它应该会运行得相当快。
我有一个 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-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>" [=10=] "</PRODUCT>"; next}
{
n++; t="FRAMESIZE"; printf " <%s%d>%s</%s%d>\n", t, n, [=10=], t, n
getline; t="CODE"; printf " <%s%d>%s</%s%d>\n", t, n, [=10=], t, n
getline; t="COLOR"; printf " <%s%d>%s</%s%d>\n", t, n, [=10=], 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>" [=12=] "</PRODUCT>"; next}
{
n++
printf "\t<PRODUCT_ITEM id=\"%d\">\n", n
t="FRAMESIZE"; printf fmt, t, [=12=], t; getline
t="CODE"; printf fmt, t, [=12=], t; getline
t="COLOR"; printf fmt, t, [=12=], 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>
I strongly recommend you figure out an XSLT solution - glenn jackman
我只能支持。所以,这是您的 XSLT 解决方案。但是,问题是:您是否展示了 代表 XML 示例,或者您的 真实 中是否有几个不同的 PRODUCT
元素] XML数据?
此外,命名元素 CODE1
、CODE2
等也可以,但我(再次强烈)建议不要这样做。我很高兴添加这个细节,但首先要澄清您是否 真的 需要这种严重的命名约定,或者您是否可以改用属性:
<CODE n="1"/>
XML 输入
正如 Glenn 已经建议的那样,必须有一个最外层元素才能使您的输入格式正确 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>
编辑:
What I missed too, that there is many different elements as Mathias asked.
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>
这是 XSLT 1.0 中的另一个解决方案 - 它假设可以有多个 <SHOPTITEM>
元素。
我添加了一个根元素 (<root>
),因为您的输入 XML 格式不正确。您也可以在此处 see/test 解决方案: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 不是太大,它应该会运行得相当快。