1

I'm currently trying to fetch the data of a simple XML-Sheet which contains data for translation use it's structured like this:

<string name="action_settings">Settings</string><!-- Comment1 -->
<string name="action_error">Something went wrong.</string>
<string name="action_menu">You've got the choice</string><!-- Comment2 -->

Sometimes there are comments after to describe the content a bit more for the translator. I'd like to get those, while I managed to write a comment, I couldn't manage to get 1 comment reliable ...

My idea is: If I want to get a comment of "action_setting" for example I use xpath to select this area:

<string name="action_settings">Settings</string>|AREASTART|<!-- Comment1 -->
|AREAEND|<string name="action_error">Something went wrong.</string>
<string name="action_menu">You've got the choice</string><!-- Comment2 -->

I already tried archieving this with this code:

<?php
    $doc = new DOMDocument();
    $doc->load('strings.xml');

    $xpath = new DOMXPath($doc);

    //foreach ($xpath->query("//string[@name='debug']/following::comment()[1]") as $comment)
    foreach ($xpath->query("/*/string[count(preceding-sibling::string[@name='debug'])=1]/comment()[1]") as $comment)
    {
        var_dump($comment->textContent." ");
    }
?>

As you can see the commented line is simply selecting every comment node after my specific element and picks the first one in row. The problem with this is that I can't make sure that the comment is really after the specific element or just the comment of an element a few lines later.(so if I'd like to get "action_error" it would give me "Comment 2" which belongs to "action_menu)

As you can see I tried already to select this desired area but it simply doesn't return anything when there's a comment.(My source: XPath select all elements between two specific elements)

So I'd be grateful if you would explain me a solution for this problem I'm facing with comments between 2 specific elements.

Community
  • 1
  • 1
Leo Pflug
  • 544
  • 3
  • 15

1 Answers1

1

You could use following-sibling in conjunction with a predicate.

Get the text of the next comment

(following-sibling::string|following-sibling::comment())[1][self::comment()]

Given a context node, for example the string with the name of action_settings:

  • (following-sibling::string|following-sibling::comment())

    Selects all of the string and comment sibling nodes following the context.

  • [1]

    Filters the node set to have all of the nodes' position() is 1: in other words, it reduces the set to only the first node.

  • [self::comment()]

    Filters the node set to have only comment nodes.

In summary, the above will return a node set consisting of either:

  • A single comment node; the one we are interested in.
  • An empty node set.

Putting it to use in an example

<?php

$xml = <<<XML
<example>
    <string name="action_settings">Settings</string><!-- Comment1 -->
    <string name="action_error">Error</string>
    <string name="action_menu">Menu</string><!-- Comment2 -->
</example>
XML;

$doc = new DOMDocument();
$doc->loadXML($xml);
$xpath = new DOMXPath($doc);

$next_comment_xpath = 'normalize-space(
    (following-sibling::string|following-sibling::comment())[1][self::comment()]
)';

$strings = $xpath->query('//string');
foreach ($strings as $string)
{
    $name = $string->getAttribute('name');
    $value = $string->nodeValue;
    $comment = $xpath->evaluate($next_comment_xpath, $string);

    echo "Name:    {$name}\n";
    echo "Value:   {$value}\n";
    echo "Comment: {$comment }\n\n";
}

The real work is done by $next_comment_xpath which uses the example location path given above. The normalize-space() is used two cast the node set to a string, for two reasons: firstly, casting to a string gets us the text content of the first node in the set, or an empty string if none and, secondly, it means evaluate() can return a PHP string.

Example output

Name:    action_settings
Value:   Settings
Comment: Comment1

Name:    action_error
Value:   Error
Comment: 

Name:    action_menu
Value:   Menu
Comment: Comment2
salathe
  • 51,324
  • 12
  • 104
  • 132