如何将分隔的字符串拆分为XML节点树

时间:2012-05-23 13:56:19

标签: php mysql xml xslt xslt-2.0

预先确定,我是PHP和Java的新手,但是在过去十年中进入管理层后试图刷新我的编码。

我有一张表格:

heading1_sub1_element1 = data1
heading1_sub1_element2 = data2
heading1_sub1_element3 = data3
heading1_sub2_element1 = data4
heading1_sub2_element2 = data5
heading1_sub2_element3 = data6

使用Tony Marsden's site上的精彩示例,我已经能够将表格提取到表格中:

<table>
    <heading1_sub1_element1>data1</heading1_sub1_element1>
    <heading1_sub1_element2>data2</heading1_sub1_element2>
    <heading1_sub1_element3>data3</heading1_sub1_element3>
    <heading1_sub2_element1>data4</heading1_sub2_element1>
    <heading1_sub2_element2>data5</heading1_sub2_element2>
    <heading1_sub2_element3>data6</heading1_sub2_element3>
</table>

但我想要的是:

<heading1>
    <sub1>
        <element1>Data1</element1>
        <element2>Data2</element2>
        <element3>Data3</element3>
    </sub1>
    <sub2>
        <element1>Data4</element1>
        <element2>Data5</element2>
        <element3>Data6</element3>
    </sub2>
</heading1>

有没有人知道如何将数据转换为该格式?我需要使用XSLT,还是PHP可以直接执行此操作?

我这样做的唯一原因是XML看起来整体负载更好。

提前致谢,任何帮助都会得到很好的回复。

3 个答案:

答案 0 :(得分:2)

这是一个通用的解决方案,能够正确处理具有指定格式的任何 - 即使每行上有不同数量的下划线,第一个“名称“在所有行上都不一样:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema"
 xmlns:my="my:my" exclude-result-prefixes="my xs">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:variable name="vLines" select="tokenize(/*, '\r?\n')[.]"/>

 <xsl:variable name="vPass1">
  <t>
   <xsl:apply-templates mode="pass1"/>
  </t>   
 </xsl:variable>


 <xsl:template match="/*" mode="pass1">
  <xsl:for-each select="$vLines">
   <xsl:sequence select="my:makeTree(normalize-space(.))"/>
  </xsl:for-each>
 </xsl:template>

 <xsl:template match="/">
  <xsl:apply-templates select="$vPass1" mode="pass2"/>
 </xsl:template>

 <xsl:function name="my:makeTree">
  <xsl:param name="pLine"/>

  <xsl:variable name="vName" select="substring-before($pLine, '_')"/>

  <xsl:choose>
    <xsl:when test="$vName">
      <xsl:element name="{$vName}">
        <xsl:sequence select="my:makeTree(substring-after($pLine, '_'))"/>
      </xsl:element>
    </xsl:when>
    <xsl:otherwise>
     <xsl:element name=
       "{normalize-space(substring-before($pLine, '='))}">
       <xsl:sequence select="substring-after($pLine, '=')"/>
     </xsl:element>
    </xsl:otherwise>
  </xsl:choose>
 </xsl:function>

 <xsl:function name="my:group">
  <xsl:param name="pNodes" as="node()*"/>

  <xsl:for-each-group select="$pNodes[self::*]" group-by="name()">
    <xsl:element name="{name()}">
      <xsl:for-each select="current-group()">
         <xsl:sequence select="my:group(node())"/>
      </xsl:for-each>
    </xsl:element>
  </xsl:for-each-group>
  <xsl:copy-of select="$pNodes[not(self::*)]"/>
 </xsl:function>

  <xsl:template match="*[not(my:path(.) = preceding::*/my:path(.))]" mode="pass2">
  <xsl:copy>
   <xsl:apply-templates select="//*[my:path(.) = my:path((current()))]/node()"
        mode="pass2"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="*" mode="pass2"/>

 <xsl:template match="/*" mode="pass2" priority="3">
   <xsl:apply-templates mode="pass2"/>
 </xsl:template>

 <xsl:function name="my:path" as="xs:string">
  <xsl:param name="pElement" as="element()"/>

  <xsl:sequence select=
   "string-join($pElement/ancestor-or-self::*/name(.), '/')"/>
 </xsl:function>

</xsl:stylesheet>

将此转换应用于以下XML文档(给定的行,包装到顶部元素中以使其成为格式良好的XML文档):

<t>
    heading1_sub1_element1 = data1
    heading1_sub1_element2 = data2
    heading1_sub1_element3 = data3
    heading1_sub2_element1 = data4
    heading1_sub2_element2 = data5
    heading1_sub2_element3 = data6
</t>

产生了想要的正确结果:

<heading1>
   <sub1>
      <element1> data1</element1>
      <element2> data2</element2>
      <element3> data3</element3>
   </sub1>
   <sub2>
      <element1> data4</element1>
      <element2> data5</element2>
      <element3> data6</element3>
   </sub2>
</heading1>

将相同的转换应用于此更复杂的XML文档

<t>
    heading1_sub1_element1 = data1
    heading1_sub1_element2 = data2
    heading1_sub1_element3 = data3
    heading1_sub2_element1 = data4
    heading1_sub2_element2 = data5
    heading1_sub2_element3 = data6
    heading2_sub1_sub2_sub3 = data7
    heading2_sub1_sub2_sub3_sub4 = data8
    heading2_sub1_sub2 = data9
    heading2_sub1 = data10
    heading2_sub1_sub2_sub3 = data11
</t>

我们再次获得正确的,想要的结果

<heading1>
   <sub1>
      <element1> data1</element1>
      <element2> data2</element2>
      <element3> data3</element3>
   </sub1>
   <sub2>
      <element1> data4</element1>
      <element2> data5</element2>
      <element3> data6</element3>
   </sub2>
</heading1>
<heading2>
   <sub1>
      <sub2>
         <sub3>
            data7
            <sub4> data8</sub4>
            data11
         </sub3>
         data9
      </sub2>
      data10
  </sub1>
</heading2>

<强>解释

这是一个两遍处理:

  1. 在pass1中,我们将输入转换为临时树(在上面第一个XML文档的情况下):
  2. .....

    <t>
       <heading1>
          <sub1>
             <element1> data1</element1>
          </sub1>
       </heading1>
       <heading1>
          <sub1>
             <element2> data2</element2>
          </sub1>
       </heading1>
       <heading1>
          <sub1>
             <element3> data3</element3>
          </sub1>
       </heading1>
       <heading1>
          <sub2>
             <element1> data4</element1>
          </sub2>
       </heading1>
       <heading1>
          <sub2>
             <element2> data5</element2>
          </sub2>
       </heading1>
       <heading1>
          <sub2>
             <element3> data6</element3>
          </sub2>
       </heading1>
    </t>
    

    0.2。在第二遍中,我们执行特定类型的分组,以便生成想要的结果。

    注意:在此解决方案中,我们将输入字符串作为XML文档中唯一元素的唯一文本节点子元素。这实际上并不是必需的,我这样做只是为了方便。我们可以使用标准的XSLT 2.0函数 unparsed-text() 从外部文本文件中读取字符串。

答案 1 :(得分:1)

就个人而言,除非你真的与你制作的XML的第一版绑定,否则我将从原始文本文件格式转换并在php中转换整个内容以创建所需的XML。 是的,您可以使用XSL从第一个转换为第二个,但实际上,将这些原始字符串拆分为键值对是一个更简单的过程,然后将其作为常规表达式或字符串拆分,将字符串拆分为'_'字符并将其用作XML结构。 如果你使用

$vals = explode("_", $string_input);

只关键,这将为您提供第一个:

$vals[0] = "heading1";
$vals[1] = "sub1";
$vals[2] = "element1";

你可以用它来制作你想要的结构

我通常不建议有人用字符串创建XML结构(当你遇到编码问题时),但如果你确定不会,只需将其作为字符串输出(或者如同其他答案所说,的SimpleXML)。

答案 2 :(得分:0)

我很确定你必须做一些字符串操作来解析你想要的节点名称,但之后再看看PHP的simpleXML。这是一个显示如何使用它的good answer