在PHP中获取XML / XMP标记的值

时间:2015-07-07 05:55:22

标签: php xml xmp

我有这个XMP字符串$ xml:

<x:xmpmeta xmlns:x="adobe:ns:meta/">
  <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
    <rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:xmp="http://ns.adobe.com/xap/1.0/">
      <xmp:CreatorTool>Microsoft Photo Gallery 16.4.3528.331</xmp:CreatorTool>
      <xmp:Rating>2</xmp:Rating>
    </rdf:Description>
    <rdf:Description rdf:about="uuid:faf5bdd5-ba3d-11da-ad31-d33d75182f1b" xmlns:MP="http://ns.microsoft.com/photo/1.2/">
      <MP:RegionInfo>
        <rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
          <MPRI:Regions xmlns:MPRI="http://ns.microsoft.com/photo/1.2/t/RegionInfo#">
            <rdf:Bag xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
              <rdf:li>
                <rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                  <MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.144259, 0.358824, 0.065751, 0.098529</MPReg:Rectangle>
                </rdf:Description>
              </rdf:li>
              <rdf:li>
                <rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                  <MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.211973, 0.294118, 0.023553, 0.035294</MPReg:Rectangle>
                </rdf:Description>
              </rdf:li>
              <rdf:li>
                <rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                  <MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.350343, 0.423529, 0.056919, 0.085294</MPReg:Rectangle>
                  <MPReg:PersonDisplayName xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">xc</MPReg:PersonDisplayName>
                </rdf:Description>
              </rdf:li>
              <rdf:li>
                <rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                  <MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.352306, 0.300000, 0.023553, 0.035294</MPReg:Rectangle>
                </rdf:Description>
              </rdf:li>
              <rdf:li>
                <rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                  <MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.395486, 0.304412, 0.047105, 0.070588</MPReg:Rectangle>
                </rdf:Description>
              </rdf:li>
              <rdf:li>
                <rdf:Description xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
                  <MPReg:Rectangle xmlns:MPReg="http://ns.microsoft.com/photo/1.2/t/Region#">0.823356, 0.560294, 0.095191, 0.142647</MPReg:Rectangle>
                </rdf:Description>
              </rdf:li>
            </rdf:Bag>
          </MPRI:Regions>
        </rdf:Description>
      </MP:RegionInfo>
    </rdf:Description>
    <rdf:Description xmlns:MicrosoftPhoto="http://ns.microsoft.com/photo/1.0/">
      <MicrosoftPhoto:Rating>25</MicrosoftPhoto:Rating>
    </rdf:Description>
    <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/">
      <dc:title>
        <rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
          <rdf:li xml:lang="x-default">edgf</rdf:li>
        </rdf:Alt>
      </dc:title>
    </rdf:Description>
    <rdf:Description xmlns:dc="http://purl.org/dc/elements/1.1/">
      <dc:description>
        <rdf:Alt xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
          <rdf:li xml:lang="x-default">edgf</rdf:li>
        </rdf:Alt>
      </dc:description>
    </rdf:Description>
  </rdf:RDF>
</x:xmpmeta>

我对2个标签感兴趣:MPReg:Rectangle和MPReg:PersonDisplayName。 我想只在同一个标​​签中有PersonDisplayname才能读取Rectangle值。

我尝试使用以下代码将XMP转换为数组:

function get_xmp_array( &$xmp_raw ) {
            $xmp_arr = array();
            foreach ( array(
                    'RectangleCoords' => '<MPReg:Rectangle[^>]+?xmlns:MPReg="([^"]*)"',
                    'attempt2' => '<MPReg:Rectangle>\s*(.*?)\s*<\/MPReg:Rectangle>'
            ) as $key => $regex ) {

                    // get a single text string
                    $xmp_arr[$key] = preg_match( "/$regex/is", $xmp_raw, $match ) ? $match[1] : '';

                    // if string contains a list, then re-assign the variable as an array with the list elements
                    $xmp_arr[$key] = preg_match_all( "/<rdf:li[^>]*>([^>]*)<\/rdf:li>/is", $xmp_arr[$key], $match ) ? $match[1] : $xmp_arr[$key];

                    // hierarchical keywords need to be split into a third dimension
                    if ( ! empty( $xmp_arr[$key] ) && $key == 'Hierarchical Keywords' ) {
                            foreach ( $xmp_arr[$key] as $li => $val ) $xmp_arr[$key][$li] = explode( '|', $val );
                            unset ( $li, $val );
                    }
            }
            return $xmp_arr;
    }

但它没有用,它返回了这个:

'RectangleCoords' => string 'http://ns.microsoft.com/photo/1.2/t/Region#'
'attempt2' => string ''

我尝试了多种功能,如:

function getTextBetweenTags($string, $tagname) {
    $pattern = "/<$tagname ?.*>(.*)<\/$tagname>/";
    preg_match($pattern, $string, $matches);
    return $matches[1];
}

此功能仅返回第一场比赛,我不知道如何获得所有比赛。

我也试过这个:

$doc = new DOMDocument();
$doc->loadXML($xml);
$result = $doc->getElementsByTagName('MPReg:Rectangle');
var_dump( $result );

但它什么都没有回复:

object(DOMNodeList)[3]

我真的很感谢你对此的帮助。

谢谢

2 个答案:

答案 0 :(得分:2)

不要使用正则表达式来解析XML。使用XML解析器(DOM)和Xpath。 Xpath是一种用于选择DOM节点的表达式语言。

首先创建一个DOM文档,加载XML并为文档创建一个Xpath实例。

$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);

XML使用名称空间,因此现在必须为它们注册前缀别名。 XML中的别名仅对文档有效。在解析时,DOM解析名称空间。您可以将根节点读为{adobe:ns:meta/}:xmpmeta

$xpath->registerNamespace('x', 'adobe:ns:meta/');
$xpath->registerNamespace('xmp', 'http://ns.adobe.com/xap/1.0/');
$xpath->registerNamespace('rdf', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
$xpath->registerNamespace('MP', 'http://ns.microsoft.com/photo/1.2/');
$xpath->registerNamespace('MPRI', 'http://ns.microsoft.com/photo/1.2/t/RegionInfo#');
$xpath->registerNamespace('MPReg', 'http://ns.microsoft.com/photo/1.2/t/Region#');

这允许Xpath实例解析名称空间。表达式/x:xmpmeta可以解析为/{adobe:ns:meta/}:xmpmeta并匹配根节点,即使名称空间前缀/别名不同。

现在您可以使用DOMXpath::evaluate()来获取节点和值:

foreach ($xpath->evaluate('//MPRI:Regions//rdf:Description') as $description) {
  var_dump(
    [
      'rectangle' => $xpath->evaluate('string(MPReg:Rectangle)', $description),
      'person' => $xpath->evaluate('string(MPReg:PersonDisplayName)', $description),
    ]
  );
}

表达式//MPRI:Regions//rdf:Description获取mpri区域元素节点内的所有rdf描述元素。对于每个描述,两个表达式将矩形(string(MPReg:Rectangle))和人物显示名称(string(MPReg:PersonDisplayName))取为字符串。

输出:

array(2) {
  ["rectangle"]=>
  string(38) "0.144259, 0.358824, 0.065751, 0.098529"
  ["person"]=>
  string(0) ""
}
array(2) {
  ["rectangle"]=>
  string(38) "0.211973, 0.294118, 0.023553, 0.035294"
  ["person"]=>
  string(0) ""
}
array(2) {
  ["rectangle"]=>
  string(38) "0.350343, 0.423529, 0.056919, 0.085294"
  ["person"]=>
  string(2) "xc"
}
array(2) {
  ["rectangle"]=>
  string(38) "0.352306, 0.300000, 0.023553, 0.035294"
  ["person"]=>
  string(0) ""
}
array(2) {
  ["rectangle"]=>
  string(38) "0.395486, 0.304412, 0.047105, 0.070588"
  ["person"]=>
  string(0) ""
}
array(2) {
  ["rectangle"]=>
  string(38) "0.823356, 0.560294, 0.095191, 0.142647"
  ["person"]=>
  string(0) ""
}

答案 1 :(得分:-1)

这是我最终使用的解决方案:

//This function will return the value of a certain tag
function getTextBetweenTags($string, $tagname, $offset) {
    $pattern = "/<$tagname ?.*>(.*)<\/$tagname>/";
    preg_match($pattern, $string, $matches, PREG_OFFSET_CAPTURE, $offset);
    return $matches;
}

//Initiate variables
$People = array(array("Rectangle" => null, "PersonName" => null)); //will store the people tag informations
$coords = array(); //will store all of the offset values
$ofs = 0; //it willl temporarily store  the offset value
$i = 0; //number of results counter
$name=""; // it willl temporarily store the person's name

//It will search the XMP String for the first Rectangle result,
//Then, it will search again, but this time it will search right after the first result
//Hence the use of the offset variable, it will repeat til there is no more rectangle results
do
{
$result = getTextBetweenTags($xml, "MPReg:Rectangle", $ofs);
if( $result ) $ofs = $result[1][1]; else break;
$coords[$i] = $ofs;
$People[$i]["Rectangle"] = $result[1][0];
$i++;
}
while ( $result );

//If there is a Person name it will follow the Rectangle tag
//By that logic, and using the previous variable,
//A reverse search for the Person name will be performed
for ( $j = $i-1 ; $j >= 0 ; $j--)
{   
$result = getTextBetweenTags($xml, "MPReg:PersonDisplayName", $coords[$j]);
if( $result )
    if ($name != $result[1][0])//If the name is not the same as the previous one
    {
        $name = $result[1][0];
        $People[$j]["PersonName"] = $result[1][0];
    }
}

var_dump( $People );