du77887 2014-08-22 15:16
浏览 78

asXML()不保存更改

I have a complex xml with nested namespaces for which I'm trying to do the following:

1) Open XML File 2) Validate against a XSD Schema 3) Parse it 4) Change nodes (1 at the time, setting them either to null or other variables) 5) Saves changed xml into a new file 5) Ri-validate it against same schema as 2) and make sure an error pops up.

Now, points 1-2-3 and 5-6 are not an issue. The Change + saving into a new xml is.

XML Snippet:

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

I do the parsing on attributes using ($mypix is the XmlSimpleObject where I load the Xml):

$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>";
                          }

                } 
        }   

The parsing gives me:

Main Node: Movie
Main attribute: creationDateTime with value: 2014-05-16T14:40:41Z
Main attribute: endDateTime with value: 2015-05-16T00:00:00Z

Entering node: endDateTime and eliminating: 2015-05-16T00:00:00Z

Problem is, when I open t.xml, endDateTime is still a valid tag (definitely not empty).

=========================================================================

Things I've tried:

alternative approach using 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

Here the xpath query returns a valid value, but changing & writing to a new file fails

2nd try: save to the same file ('prova.xml') instead of 't.xml' (in case I screwed up with SimpleXMlObjects)...nothing...

Any help please?

  • 写回答

1条回答 默认 最新

  • doubi8383 2014-08-24 19:44
    关注

    Setting a variable to null does not remove, destroy, or edit the object that variable used to point to.

    You may have seen examples where this is a valid way of "cleaning up" something like a database connection object, because when you remove all references to an object, its destructor will be called. However, this is not the case here, because the object pointed at by $b is still accessible, e.g. from another call to $mypix->children()->attributes().

    The other thing you will have seen in examples is assigning a new value to a child element or attribute using syntax like $element->someChild = 'new value'; or $element['someAttribute'] = 'new value';. However, this works because SimpleXML overloads the property access (->) and array element access ([...]), in the same way as implementing __set() and ArrayAccess::offsetSet(), and your code uses neither of those.

    There is a way of using the array-access overload to delete or blank an element which you have a variable pointing at directly, which is that the offset [0] points back at the current element. Thus, you can write unset($b[0]); to delete an element or attribute completely; you can also write $b[0] = ''; to blank an element, but with an attribute as here, that leads to a fatal error (which I suspect is a bug).

    Note that when you use XPath, you are not actually reaching this self-reference, or an overloaded operator because SimpleXMLElement::xpath returns a plain array, so $xtring[0] is just a normal PHP variable. Since it's an element in that example, you could delete it using the self-reference, by writing unset($xtring[0][0]); or blank it with $xtring[0][0] = '';


    However, all that being said, your code can actually be massively simplified in order to avoid any of this being necessary. Let's take it apart line by line:

     foreach ($mypix->children() as $parent => $child)
    

    The variable $mypix here is for a larger document than you show in your sample, the sample apparently being just one entry in this loop. Note that $parent => $child here would be more appropriately named $childName => $child.

    It's also quite likely that you're only interested in children with a particular name, so the most common form of loop is foreach ($mypix->Movie as $child)

    foreach ($mypix->children()->attributes() as $a => $b)
    

    Here you ignore the progress around the outer loop completely, and go back to the whole document. SimpleXML will interpret $mypix->children()->... as $mypix->children()[0]->..., that is only ever look at the first child. You actually want foreach ($child->attributes() ....

    if ($a == "endDateTime")
    

    Since you are looking for an attribute with a particular name, you don't actually need to loop over attributes() at all, you can just access it directly as $child['endDateTime']. Note that since we're now using the overloaded [...] operator, we can make use of it to write back to or delete the attribute.

    echo $mypix->asXML("t.xml");
    

    SimpleXMLElement::asXML either returns the document as a string or saves to a file, not both. Since in the latter case it returns a boolean, echoing that result isn't likely to be very useful.

    You are also calling this function every time around the inner loop, thus saving the same file several times. You only need to do it once, when you've finished making all your modifications.


    So, here is how I would write that code:

    foreach ( $mypix->Movie as $child )
    {
         $child['endDateTime'] = null; 
         // or to remove the attribute completely: unset($child['endDateTime']);
    }
    $mypix->asXML('t.xml');
    

    Or, for the second example but without XPath (long-winded, but useful if you are changing several things at once, so don't want to "jump" to the deepest descendants in the document). Note the use of ->children($ns_uri) to switch to a different namespace.

    // 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");
    
    评论

报告相同问题?

悬赏问题

  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?