PHP:如何在XML中的每一组元素中添加缺失的标签?

时间:2021-09-03 15:42:46

This is the XML that we have right now:

这就是我们现在拥有的XML:

<persons>
    <person>
        <firstname>John</firstname>
        <surname>Doe</surname>
        <age></age>
    </person>
    <person>
        <firstname>Jane</firstname>
        <surname>Doe</surname>
        <age></age>
        <sex>Female</sex>
    </person>
</persons>

As you could see the first group of elements only has three tags namely firstname, surname, and age while the second group has an additional tag name sex.

正如您所看到的,第一组元素只有三个标记,即firstname、姓氏和年龄,而第二组则有一个附加的标记名称“sex”。

What we need is to make all the element groups in the XML contains all the tags that each group has, in this case the first group should also contain the sex tag but in a blank state as well like this:

我们需要做的是使XML中的所有元素组包含每个组拥有的所有标记,在这种情况下,第一个组也应该包含性标记,但同时处于空白状态:

<persons>
    <person>
        <firstname>John</firstname>
        <surname>Doe</surname>
        <age></age>
        <sex></sex>
    </person>
    <person>
        <firstname>Jane</firstname>
        <surname>Doe</surname>
        <age></age>
        <sex>Female</sex>
    </person>
</persons>

Also what if there's a third, fourth or on the 50th group has another new tag named nickname? In this case all the group should have the tag nickname as well but in a blank state.

还有,如果有第三个,第四个或第50个组有另一个新标签叫昵称呢?在这种情况下,所有组都应该有标记昵称,但处于空白状态。

How could I do this efficiently in PHP?

我如何在PHP中有效地做到这一点?

3 个解决方案

#1


2  

Using SimpleXML, the script makes two passes: one to find all the possible tags, the other to create empty elements:

使用SimpleXML,脚本进行两次遍历:一次查找所有可能的标记,另一次创建空元素:

$str = <<<STR

<persons>
    <person>
        <firstname>John</firstname>
        <surname>Doe</surname>
        <age></age>
    </person>
    <person>
        <firstname>Jane</firstname>
        <surname>Doe</surname>
        <age></age>
        <sex>Female</sex>
    </person>
</persons>

STR;

$xml = simplexml_load_string($str);

// Create an array of all the possible tags
$tags = array();
foreach($xml->person as $person)
{
    $current_tags = array_keys(get_object_vars($person));
    $tags = array_unique(array_merge($tags, $current_tags));
}

// Add empty tags to elements who don't have them
foreach($xml->person as $person)
{
    foreach($tags as $tag)
    {
        if(!property_exists($person, $tag))
        {
            $person->$tag = '';
        }
    }
}

// Output the new XML
echo $xml->asXML();

#2


2  

The easiest to make this maintainable over a long time (e.g. anticipating more new fields and stuff like that) would be to process the XML with an XSLT that contains all the required fields:

在很长一段时间内(例如预测更多的新字段之类的),最容易的方法是使用包含所有必需字段的XSLT处理XML:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/persons">
    <persons>
      <xsl:apply-templates select="person"/>
    </persons>
  </xsl:template>

  <xsl:template match="person">
    <person>
        <firstname><xsl:value-of select="firstname"/></firstname>
        <surname><xsl:value-of select="surname"/></surname>
        <age><xsl:value-of select="age"/></age>
        <sex><xsl:value-of select="sex"/></sex>
    </person>
  </xsl:template>

</xsl:stylesheet>

Then, whenever you get a new copy from the generator service, do (manual)

然后,无论何时您从生成器服务获得新的副本,请执行(手动)

$dom = new DOMDocument;
$dom->load('people.xml');
$xsl = new DOMDocument;
$xsl->load('people.xsl');
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
echo $proc->transformToXML($dom);

This will produce (demo)

这将产生(演示)

<?xml version="1.0"?>
<persons>
  <person>
    <firstname>John</firstname>
    <surname>Doe</surname>
    <age/>
    <sex/>
  </person>
  <person>
    <firstname>Jane</firstname>
    <surname>Doe</surname>
    <age/>
    <sex>Female</sex>
  </person>
</persons>

#3


2  

I agree with @Gordon that the best solution here is XSLT. However, I suggest using a slightly different XSL:

我同意@Gordon的观点,这里最好的解决方案是XSLT。但是,我建议使用稍微不同的XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="person">
        <person>
            <xsl:apply-templates select="@* | *[not(self::sex)]"/>
            <sex><xsl:value-of select="sex"/></sex>
        </person>
    </xsl:template>
</xsl:stylesheet>

I tried it out with W3Schools' online XSLT evaluator and it works as requested.

我在W3Schools的在线XSLT评估器上试用了它,并按要求工作。

#1


2  

Using SimpleXML, the script makes two passes: one to find all the possible tags, the other to create empty elements:

使用SimpleXML,脚本进行两次遍历:一次查找所有可能的标记,另一次创建空元素:

$str = <<<STR

<persons>
    <person>
        <firstname>John</firstname>
        <surname>Doe</surname>
        <age></age>
    </person>
    <person>
        <firstname>Jane</firstname>
        <surname>Doe</surname>
        <age></age>
        <sex>Female</sex>
    </person>
</persons>

STR;

$xml = simplexml_load_string($str);

// Create an array of all the possible tags
$tags = array();
foreach($xml->person as $person)
{
    $current_tags = array_keys(get_object_vars($person));
    $tags = array_unique(array_merge($tags, $current_tags));
}

// Add empty tags to elements who don't have them
foreach($xml->person as $person)
{
    foreach($tags as $tag)
    {
        if(!property_exists($person, $tag))
        {
            $person->$tag = '';
        }
    }
}

// Output the new XML
echo $xml->asXML();

#2


2  

The easiest to make this maintainable over a long time (e.g. anticipating more new fields and stuff like that) would be to process the XML with an XSLT that contains all the required fields:

在很长一段时间内(例如预测更多的新字段之类的),最容易的方法是使用包含所有必需字段的XSLT处理XML:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/persons">
    <persons>
      <xsl:apply-templates select="person"/>
    </persons>
  </xsl:template>

  <xsl:template match="person">
    <person>
        <firstname><xsl:value-of select="firstname"/></firstname>
        <surname><xsl:value-of select="surname"/></surname>
        <age><xsl:value-of select="age"/></age>
        <sex><xsl:value-of select="sex"/></sex>
    </person>
  </xsl:template>

</xsl:stylesheet>

Then, whenever you get a new copy from the generator service, do (manual)

然后,无论何时您从生成器服务获得新的副本,请执行(手动)

$dom = new DOMDocument;
$dom->load('people.xml');
$xsl = new DOMDocument;
$xsl->load('people.xsl');
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
echo $proc->transformToXML($dom);

This will produce (demo)

这将产生(演示)

<?xml version="1.0"?>
<persons>
  <person>
    <firstname>John</firstname>
    <surname>Doe</surname>
    <age/>
    <sex/>
  </person>
  <person>
    <firstname>Jane</firstname>
    <surname>Doe</surname>
    <age/>
    <sex>Female</sex>
  </person>
</persons>

#3


2  

I agree with @Gordon that the best solution here is XSLT. However, I suggest using a slightly different XSL:

我同意@Gordon的观点,这里最好的解决方案是XSLT。但是,我建议使用稍微不同的XSL:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="person">
        <person>
            <xsl:apply-templates select="@* | *[not(self::sex)]"/>
            <sex><xsl:value-of select="sex"/></sex>
        </person>
    </xsl:template>
</xsl:stylesheet>

I tried it out with W3Schools' online XSLT evaluator and it works as requested.

我在W3Schools的在线XSLT评估器上试用了它,并按要求工作。