I have a xml file and I am confused how to find translation tag using the id tag which lie inside a message element.
What is the easiest way to look in name=menu and print the translation whose id=1
Here is my xml:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="en_GB" sourcelanguage="en_GB">
<context>
<name>Menu</name>
<message>
<id>1</id>
<source>Home</source>
<translation>Home</translation>
</message>
<message>
<id>25</id>
<source>About</source>
<translation>About</translation>
</message>
</context>
<context>
<name>Web</name>
<message>
<id>59</id>
<source>Welcome to </source>
<translation>Welcome to </translation>
</message>
<message>
<id>68</id>
<source>Happy</source>
<translation>Happy</translation>
</message>
</context>
</TS>
Use a XML parser for this. In this example, I will use built-in DOMDocument with DOMXPath:
$dom = new DomDocument();
$dom->loadXML( $xml );
$xpath = new DOMXPath( $dom );
$nodes = $xpath->query( '//context/message/id[.=25]/../translation' );
echo $nodes->item(0)->nodeValue;
Will print:
About
->loadXML( $xml ) loads your XML string, to load a file use ->load( $filePath ) instead.
The xpath syntax allow to performs short, complex queries. Above xpath means:
/ Root element
TS/context/message Descendant tree
/id[.=25] tag `<id>` with 25 as node value
/.. `<id>` parent tag (<message>)
/translation `<translation>` child
With this query we select all nodes with that matches above pattern (one node, in your case); the result is a group of nodes, so to refer to a node we have to use ->item(n) syntax. To extract its content, we use ->nodeValue.
In my example I use a complete-tree syntax, but in your XML <id> is in unique tree position, so you can short xpath query in this way:
//id[.=25]/../translation
The // at start means “Select following pattern no matter where they are in the document”.
Related
I have an xml like so:
<?xml version="1.0" encoding="UTF-8"?>
<foo SeqNum="1">
<bar>1234</bar>
</foo>
<foo SeqNum="20">
<bar>6789</bar>
</foo>
and I'm trying to get the value 6789 with this query:
$xml = "<?xml version="1.0" encoding="UTF-8"?>
<foo SeqNum="1">
<bar>1234</bar>
</foo>
<foo SeqNum="20">
<bar>6789</bar>
</foo>";
$simple = new SimpleXMLElement($xml);
$result = $simple->xpath('//*[#SeqNum="20"]/bar/'); // result gives me nothing
So I tried to just get the parent like so
$result = $simple->xpath('//*[#SeqNum="20"]')[0]->asXML();
which gives me:
<foo SeqNum="20">
<bar>6789</bar>
</foo>
So I'm almost there but am really stuck about what I'm not understanding. Thank you!
Here are several mistakes in the question. The XML needs a root element and the trailing / breaks the expression. The literal quotes need to be changed to single quotes (or all the inner double quotes need to be escaped.)
Fixed example:
$xml = '<?xml version="1.0" encoding="UTF-8"?>
<foo>
<foo SeqNum="1">
<bar>1234</bar>
</foo>
<foo SeqNum="20">
<bar>6789</bar>
</foo>
</foo>';
$simple = new SimpleXMLElement($xml);
$result = $simple->xpath('//*[#SeqNum="20"]/bar');
var_dump((string)$result[0]);
Output:
string(4) "6789"
With Namespaces
If your XML is using namespaces you will have to define an alias/prefix for this namespace URI and use that in the Xpath expression.
$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8" ?>
<p:foo xmlns:p="http://www.example.com">
<p:foo SeqNum="1">
<p:bar>1234</p:bar>
</p:foo>
<p:foo SeqNum="20">
<p:bar>6789</p:bar>
</p:foo>
</p:foo>
XML;
$simple = new SimpleXMLElement($xml);
$simple->registerXpathNamespace('e', 'http://www.example.com');
$result = $simple->xpath('//*[#SeqNum="20"]/e:bar');
var_dump((string)$result[0]);
The example uses a different alias for the expression to show that the document and the expression are separate - only the namespace URI has to match.
Namespaces have to be unique so they are defined with an URI (a superset of URL). Because that would get messy aliases are used in node names. The following 3 elements all can be read as {http://www.example.com}bar.
<p:bar xmlns:p="http://www.example.com"/>
<e:bar xmlns:e="http://www.example.com"/>
<bar xmlns="http://www.example.com"/>
I know that maybe is a duplicate but doesn't works....
I have this xml
<?xml version="1.0" encoding="UTF-8"?><kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
<Document>
<name>EMSC - Last 2 Weeks earthquakes worldwide</name>
<Folder>
<name>2017 7 - 13</name>
</Folder>
<Folder>
<name>2017 7 - 12</name>
</Folder>
</Document>
</kml>
i want count node Folder, i tied this
$pars_emsc= simplexml_load_file('/file');
$count_folder_emsc= $pars_emsc-> Document -> getElementsByTagName('Folder')->length;
but doesn't work...
You can't use the simple XML parser as a DOM document. Quite simply getElementsByTagName doesn't exist in SimpleXML. Use this instead:
$pars_emsc = new DOMDocument( "1.0", "ISO-8859-15" );
$pars_emsc->load("/file");
$count_folder_emsc= $pars_emsc->getElementsByTagName("Document")[0]->getElementsByTagName('Folder')->length;
print_r($count_folder_emsc);
Alternatively just do:
$pars_emsc= simplexml_load_file("/file");
$count_folder_emsc= $pars_emsc-> Document -> Folder -> count();
Xpath expressions can count nodes, too.
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('kml', 'http://www.opengis.net/kml/2.2');
var_dump($xpath->evaluate('count(//kml:Folder)'));
Output:
float(2)
You XML uses a default namespace, so you have to register a prefix for it.
I have to parse the following XML:
<BMECAT version="2005" xmlns="http://www.bmecat.org/bmecat/2005fd">
<T_NEW_CATALOG prev_version="0">
<PRODUCT>
<NAME>Test</NAME>
<USER_DEFINED_EXTENSIONS>
<UDX.VENDOR.ATTRIBUTE lang="de">German</UDX.VENDOR.ATTRIBUTE>
<UDX.VENDOR.ATTRIBUTE lang="en">English</UDX.VENDOR.ATTRIBUTE>
</USER_DEFINED_EXTENSIONS>
</PRODUCT>
</T_NEW_CATALOG>
</BMECAT>
I need to get the value "German" via xpath. Everything I have tried so far didn't work so I need some help. I think the problem is that the tagname includes dots. I have found no way to escape the xpath expression.
The XML you show cannot be parsed because it is malformed (USER_DEFINED_EXTENSIONS is not closed properly). Assuming well-formed input, you could use DOMDocument instead of SimpleXML:
<?php
$xml = <<<'XML'
<BMECAT version="2005" xmlns="http://www.bmecat.org/bmecat/2005fd">
<T_NEW_CATALOG prev_version="0">
<PRODUCT>
<NAME>Test</NAME>
<USER_DEFINED_EXTENSIONS>
<UDX.VENDOR.ATTRIBUTE lang="de">German</UDX.VENDOR.ATTRIBUTE>
<UDX.VENDOR.ATTRIBUTE lang="en">English</UDX.VENDOR.ATTRIBUTE>
</USER_DEFINED_EXTENSIONS>
</PRODUCT>
</T_NEW_CATALOG>
</BMECAT>
XML;
$dom = new DOMDocument();
$dom->loadXML($xml);
$xpath = new DOMXPath($dom);
$xpath->registerNamespace("bme",
"http://www.bmecat.org/bmecat/2005fd");
$expression = 'string(
/bme:BMECAT
/bme:T_NEW_CATALOG
/bme:PRODUCT
/bme:USER_DEFINED_EXTENSIONS
/bme:UDX.VENDOR.ATTRIBUTE[#lang = "de"]
)';
var_dump($xpath->evaluate($expression));
And the result will be
German
If you need to use SimpleXML, you really need to show your current PHP code.
Given a base $xml and a file containing a <something> tag with attributes, children and children of its children, I would like to append it as first child and all of its children as raw XML.
Original XML:
<root>
<people>
<person>
<name>John Doe</name>
<age>47</age>
</person>
<person>
<name>James Johnson</name>
<age>13</age>
</person>
</people>
</root>
XML in file:
<something someval="x" otherthing="y">
<child attr="val" ..> { some children and values ... }</child>
<child attr="val2" ..> { some children and values ... }</child>
...
</something>
Result XML:
<root>
<something someval="x" otherthing="y">
<child attr="val" ..> { some children and values ... }</child>
<child attr="val2" ..> { some children and values ... }</child>
...
</something>
<people>
<person>
<name>John Doe</name>
<age>47</age>
</person>
<person>
<name>James Johnson</name>
<age>13</age>
</person>
</people>
</root>
This tag would contain several children both direct and recursively, so it would not be practical to build the XML via the SimpleXML operations. Besides, keeping it in a file would result in lower maintenance costs.
Technically it would simply be prepending one child. The problem is that this child would have other children and so on.
On the PHP addChild page there's a comment that says:
$x = new SimpleXMLElement('<root name="toplevel"></root>');
$f1 = new SimpleXMLElement('<child pos="1">alpha</child>');
$x->{$f1->getName()} = $f1; // adds $f1 to $x
However, this does not seem to treat my XML as raw XML therefore causing < and > escaped tags to appear. Several warnings concerning namespaces seem to appear as well.
I suppose I could do a quick replace of such tags but I am not sure whether it could cause future problems and it certainly does not feel right.
Manually hacking the XML is not an option and neither is adding children one by one. Choosing a different library could be.
Any clues on how to get this working?
Thanks!
I'm really not sure if that will work. Try this or downvote this, but I hope it helps. Using DOMDocument (Reference)
<?php
$xml = new DOMDocument();
$xml->loadHTML($yourOriginalXML);
$newNode = DOMDocument::createElement($someXMLtoPrepend);
$nodeRoot = $xml->getElementsByTagName('root')->item(0);
$nodeOriginal = $xml->getElementsByTagName('people')->item(0);
$nodeRoot->insertBefore($newNode,$nodeOriginal);
$finalXmlAsString = $xml->saveXML();
?>
Sometimes UTF-8 can make problems, then try this:
<?php
$xml = new DOMDocument();
$xml->loadHTML(mb_convert_encoding($yourOriginalXML, 'HTML-ENTITIES', 'UTF-8'));
$newNode = DOMDocument::createElement(mb_convert_encoding($someXMLtoPrepend, 'HTML-ENTITIES', 'UTF-8'));
$nodeRoot = $xml->getElementsByTagName('root')->item(0);
$nodeOriginal = $xml->getElementsByTagName('people')->item(0);
$nodeRoot->insertBefore($newNode,$nodeOriginal);
$finalXmlAsString = $xml->saveXML();
?>
I need to add in a new root node to the following XML
<?xml version="1.0"?>
<unit>
<source>
<id>ANCH02</id>
<uri>http://www.hamiltonisland.biz/tabid/339/Default.aspx</uri>
</source>
</unit>
to become
<?xml version="1.0"?>
<units>
<unit>
<source>
<id>ANCH02</id>
<uri>http://www.hamiltonisland.biz/tabid/339/Default.aspx</uri>
</source>
</unit>
</units>
How could I do this? It doesn't seem like SimpleXMLElement has this functionality. I have also looked at this DomNode example http://php.net/manual/en/domnode.insertbefore.php but it doesnt seem to be able to add in a new root node.
This seem to work
$units = $dom->createElement('units');
$units->appendChild($dom->documentElement);
$dom->appendChild($units);
DEMO
DOMDocument:
$yourDOMDOMDocument ... <--- already loaded XML
$doc = new DOMDocument();
$doc->appendChild($doc->createElement('Units'));
$doc->documentElement->appendChild($doc->importNode($yourDOMDocument->documentElement));
Or. if you have your XML as SimpleXMLElement already:
$yourSimpleXML ... <--- already loaded XML
$doc = new DOMDocument();
$doc->appendChild($doc->createElement('Units'));
$domnode = dom_import_simplexml($yourSimpleXML);
$doc->documentElement->appendChild($doc->importNode($domnode));
//if you want it back as SXE:
$newSimpleXMLElement = simplexml_import_dom($doc);