我有一个复杂的xml嵌套命名空间,我试图做以下事情:
1)打开XML文件2)根据XSD模式进行验证3)解析4)更改节点(每次1个,将它们设置为null或其他变量)5)保存更改的xml到一个新的文件5)对与2)相同的模式进行i-验证,并确保弹出错误。
现在,1-2-3和5-6点不是问题。Change +保存到新的xml文件是。
XML代码片段:
<Movie creationDateTime="2014-05-14T13:42:52Z" endDateTime="2015-05-14T00:00:00Z" providerVersionNum="5" startDateTime="2014-05-14T00:00:00Z" uriId="disney.chlsd.com/MOOT0000000000020902">
<core:Ext>
<ext:MovieExtensions analogueOff="true" mediaId="CGOT0000000000020902">
<ext:assetPart partNum="1">
<ext:SourceUrl>DSNY0000000000020902.mxf</ext:SourceUrl>
<ext:ContentFileSize>46166173874</ext:ContentFileSize>
<ext:ContentCheckSum>4da3e4cafd4f3262d136c519311a7b53</ext:ContentCheckSum>
<ext:SOE>PT09H59M30S00F</ext:SOE>
<ext:SOM>PT10H00M00S00F</ext:SOM>
<ext:EOM>PT10H46M02S11F</ext:EOM>
</ext:assetPart>
<ext:playlistSupportOnly>false</ext:playlistSupportOnly>
</ext:MovieExtensions>
</core:Ext>
<content:AudioType>Stereo</content:AudioType>
<content:FrameRate>25</content:FrameRate>
<content:Codec>H.264</content:Codec>
<content:AVContainer>MXF</content:AVContainer>
<content:Duration>PT00H46M02S</content:Duration>
<content:IsHDContent>false</content:IsHDContent>
</Movie>
我使用($mypix是我加载Xml的XmlSimpleObject)对属性进行解析:
$xmlfile = "prova.xml";
$mypix = simplexml_load_file($xmlfile);
[…]
foreach ($mypix->children() as $parent => $child)
{
echo "<br/>Main Node: ".(String)$parent."<br/>";
foreach ($mypix->children()->attributes() as $a => $b)
{
echo "Main attribute: ".(String)$a. " with value: ".(String)$b."<br/>";
if ($a == "endDateTime")
{
echo "Entering node: ".$a." and eliminating: ".$b." <br/>";
$b=NULL;
echo "<br/><pre>";
echo $mypix->asXML("t.xml");
echo "<br/></pre>";
}
}
}
解析得到:
Main Node: Movie
Main attribute: creationDateTime with value: 2014-05-16T14:40:41Z
Main attribute: endDateTime with value: 2015-05-16T00:00:00Z
进入节点:endDateTime并消除:2015-05-16T00:00:00Z
问题是,当我打开t.xml时,endDateTime仍然是一个有效的标记(绝对不是空的)。
=========================================================================
我尝试过的事情:
使用Xpath的替代方法:
$namespaces = $mypix->getNameSpaces(true);
$mypix->registerXPathNamespace('ext', 'URN:NNDS:CMS:ADI3:01');
$mypix->registerXPathNamespace('title', 'http://www.cablelabs.com/namespaces/metadata/xsd/title/1');
$mypix->registerXPathNamespace('core', 'http://www.cablelabs.com/namespaces/metadata/xsd/core/1');
echo "<br/><br/>";
// Getting Episode Name
$xtring = ($mypix->xpath('//core:Ext/ext:LocalizableTitleExt/ext:EpisodeName'));
echo "<br/><b>EpisodeName: </b>".$xtring[0]."<br/>";
$xtring[0] = NULL;
echo $mypix->asXML("t.xml"); // Nothing again
这里xpath查询返回一个有效值,但是改变了&写入新文件失败
第二次尝试:保存到相同的文件('prova.xml')而不是' .xml'(以防我搞砸了SimpleXMlObjects)…没有…
请帮忙好吗?
将变量设置为null
不会删除、销毁或编辑该变量曾经指向的对象。
您可能已经看到过这样的例子,其中这是"清理"诸如数据库连接对象之类的东西的有效方法,因为当您删除对对象的所有引用时,将调用其析构函数。然而,这里的情况并非如此,因为$b
所指向的对象仍然是可访问的,例如,从另一个调用$mypix->children()->attributes()
。
您将在示例中看到的另一件事是使用$element->someChild = 'new value';
或$element['someAttribute'] = 'new value';
之类的语法为子元素或属性分配新值。然而,这是有效的,因为SimpleXML重载属性访问(->
)和数组元素访问([...]
),以实现__set()
和ArrayAccess::offsetSet()
相同的方式,而您的代码都不使用它们。
是一种使用数组访问重载来删除或清空一个变量直接指向的元素的方法,即偏移量[0]
指向当前元素。因此,您可以编写unset($b[0]);
来完全删除元素或属性;您还可以将$b[0] = '';
写入空白元素,但是如果使用此处的属性,则会导致致命错误(我怀疑这是一个错误)。
注意,当您使用XPath时,您实际上并没有到达这个自引用或重载运算符,因为SimpleXMLElement::xpath
返回一个普通数组,因此$xtring[0]
只是一个普通的PHP变量。由于它是该示例中的一个元素,您可以使用自引用删除它,通过写入unset($xtring[0][0]);
或使用$xtring[0][0] = '';
然而,话虽如此,你的代码实际上可以大量简化,以避免任何必要的。让我们逐行拆分:
foreach ($mypix->children() as $parent => $child)
这里的变量$mypix
用于比示例中显示的更大的文档,示例显然只是此循环中的一个条目。注意,此处的$parent => $child
应该更恰当地命名为$childName => $child
。
很可能您只对具有特定名称的孩子感兴趣,因此最常见的循环形式是foreach ($mypix->Movie as $child)
foreach ($mypix->children()->attributes() as $a => $b)
在这里,您完全忽略外部循环的进度,并返回到整个文档。SimpleXML将$mypix->children()->...
解释为$mypix->children()[0]->...
,即只查看第一个子节点。你需要的是foreach ($child->attributes() ...
if ($a == "endDateTime")
由于您正在寻找具有特定名称的属性,因此实际上根本不需要循环attributes()
,您可以直接作为$child['endDateTime']
访问它。注意,由于现在使用的是重载的[...]
操作符,因此可以使用它来回写或删除该属性。
echo $mypix->asXML("t.xml");
SimpleXMLElement::asXML
要么以字符串形式返回文档,要么保存到文件中,而不是两者都保存。因为在后一种情况下它返回一个布尔值,所以echo
返回结果可能不是很有用。
每次在内部循环周围都调用这个函数,从而多次保存相同的文件。你只需要做一次,当你完成了所有的修改。
那么,我是这样写代码的:
foreach ( $mypix->Movie as $child )
{
$child['endDateTime'] = null;
// or to remove the attribute completely: unset($child['endDateTime']);
}
$mypix->asXML('t.xml');
或者,对于第二个示例,但没有使用XPath(冗长,但如果您一次更改多个内容,因此不想"跳转"到文档中最深处的后代,则很有用)。注意使用->children($ns_uri)
切换到不同的名称空间。
// Constants for handier but implementation-independent reference to namespaces
define('XMLNS_EXT', 'URN:NNDS:CMS:ADI3:01');
define('XMLNS_TITLE', 'http://www.cablelabs.com/namespaces/metadata/xsd/title/1');
define('XMLNS_CORE', 'http://www.cablelabs.com/namespaces/metadata/xsd/core/1');
foreach ( $mypix->children() as $child )
{
foreach ( $child->children(XMLNS_CORE)->Ext as $ext )
{
foreach ( $ext->children(XMLNS_EXT)->LocalizableTitleExt as $title )
{
// Delete a child node; note not ->children() as "ext" namespace already selected
unset($title->EpisodeName);
}
}
}
$mypix->asXML("t.xml");