Получить значение тега XML / XMP в PHP

1

У меня есть эта строка 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. Я хочу прочитать значение Rectangle только в том случае, если есть имя PersonDisplay в том же теге.

Я попытался преобразовать 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]

Я бы очень признателен за вашу помощь в этом.

спасибо

  • 1
    DOMDocument::getElementsByTagNameNS будет правильным способом получить эти элементы пространства имен .
Теги:
xmp

2 ответа

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 выбирает все элементы описания rdf внутри узла элемента областей mpri. Для каждого описания два выражения string(MPReg:Rectangle) прямоугольник (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) ""
}
-2

Это решение, в результате которого я использовал:

//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 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 );

Ещё вопросы

Сообщество Overcoder
Наверх
Меню