合并XML中的条目

时间:2015-01-15 17:54:05

标签: php xml bash xslt

我有一个包含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中,但也可以生成一个我可以用PHP处理的新XML文件:

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

3 个答案:

答案 0 :(得分: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>

答案 1 :(得分:2)

  

强烈建议你找出一个XSLT解决方案 - glenn jackman

我只能说那个。所以,这是您的XSLT解决方案。但问题是:您是否展示了代表 XML示例,或者真实 XML数据中有多个不同的PRODUCT元素?

此外,可以完成命名元素CODE1CODE2等,但我会(再次强烈地)建议不要这样做。我很高兴添加这个细节,但首先澄清一下,如果真的需要这个严格的命名约定,或者你是否可以使用属性:

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

修改

  

我也错过了,Mathias要求有许多不同的元素。

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>

答案 2 :(得分: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不是太大,它应该可以合理地快速运行。