I have the following XML file:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<person>
<id>1</id>
<name>Jane</name>
<surname>Smith</surname>
</person>
<person>
<id>2</id>
<name>John</name>
<surname>Doe</surname>
</person>
</root>
And I have the following CSV file:
id;phone
1;12345678
2;78903456
I work with PHP. I need to do with XML something like this:
Add a phone number element to the person where id is...
For example: Add a phone element with value 12345678 to the person element with id 1.
As the content of the XML will vary, it will probably be easier to XPath to find the entry you want to update...
$telephoneList = [["id"=> 1, "phone" => "12345678"],
["id"=> 2, "phone" => "78903456"]];
$xml = simplexml_load_file("a.xml");
foreach ( $telephoneList as $telephone) {
$person = $xml->xpath("//person[id={$telephone['id']}]");
if ( count($person) == 1 ) {
$person[0]->addChild("phone", $telephone['phone']);
}
}
echo $xml->asXML();
This tries to find the <person> element with an <id> with the value from the csv. If this is found, it will add in the phone number using addChild()
It's just a case of reading in the CSV file and process it as above.
With SimpleXML, you can use the addChild() method.
$file = 'xml/config.xml';
$xml = simplexml_load_file($file);
$galleries = $xml->galleries;
$gallery = $galleries->addChild('gallery');
$gallery->addChild('name', 'a gallery');
$gallery->addChild('filepath', 'path/to/gallery');
$gallery->addChild('thumb', 'mythumb.jpg');
$xml->asXML($file);
Be aware that SimpleXML will not "format" the XML for you, however going from an unformatted SimpleXML representation to neatly indented XML is not a complicated step and is covered in lots of questions here.
You can loop the $xml->children() from the SimpleXMLElement and then check if for (string)$a->id === "1". Then use addChild to add your phone element with value 12345678 to the person element.
foreach ($xml->children() as $a) {
if ((string)$a->id === "1") {
$a->addChild("phone", "12345678");
}
}
Demo
Related
I am trying to read an XML file in PHP, edit some values and save it back.
I do it by opening the XML file in php. I then convert it using SimpleXML into an array. After doing the manipulation needed, I am struggling in returning that array into the XML file in the same format due to how my XML elements are converted into attributes. Hence when I go from array to XML, my elements (which are attributes now) are saved as attributes in the updated XML file. I would like to know if it's possible to preserve XML elements when converting back from php array to XML.
A random XML example with two elements, lets call it myFile.xml
<XML>
<Project Element1 = 'some random value' Element2='Will be stored as attribute instead'>
</XML>
The php code I would run to convert it into an array
<?php
$xml = simplexml_load_file("myFile.xml") or die("Error: Cannot create object");
$arrayXML = json_decode(json_encode((array)$xml), TRUE);
$arrayXML["Project"]["attributes"]["Element1"] = "updated value"
// I will then run some array to XML converter code here found online
// took it from here https://stackoverflow.com/questions/1397036/how-to-convert-array-to-simplexml
function array_to_xml( $data, &$xml_data ) {
foreach( $data as $key => $value ) {
if( is_array($value) ) {
if( is_numeric($key) ){
$key = 'item'.$key; //dealing with <0/>..<n/> issues
}
$subnode = $xml_data->addChild($key);
array_to_xml($value, $subnode);
} else {
$xml_data->addChild("$key",htmlspecialchars("$value"));
}
}
}
$xml_data = new SimpleXMLElement();
array_to_xml($arrayNexus,$xml_data);
saving generated xml file;
$result = $xml_data->asXML('myFile.xml');
?>
Something like this would then generate an XML file like this
<XML>
<Project>
<attribute>
<Element1>updated value</Element1>
<Element2><Will be stored as attribute instead</Element2>
</attribute>
</Project>
</XML>
When the result I would like to have would be
<XML>
<Project Element1 = 'updated value' Element2='Will be stored as attribute instead'>
</XML>
I could write my own XML converter but if there exist already methods out there, can someone show me the way?
Don't convert the XML - you will loose data if you don't use specific formats like JsonML. It is much easier to use DOM. Use Xpath expressions to fetch the nodes and modify them.
$document = new DOMDocument();
$document->loadXML($xml);
$xpath = new DOMXpath($document);
// iterate the first 'Project' element
foreach($xpath->evaluate('(/XML/Project)[1]') as $project) {
// change the attribute value
$project->setAttribute('Element1', 'updated value');
}
echo $document->saveXML();
Output:
<?xml version="1.0"?>
<XML>
<Project Element1="updated value" Element2="Will be stored as attribute instead"/>
</XML>
Xpath
'XML' document element/XML
'Project' child elements/XML/Project
Limit to first found node(/XML/Project)[1]
This example uses the position in the result list as a condition but if the project has an id attribute you could use this to find the element: /XML/Project[#id="example-id"].
I have an XML structure like this
<companies>
<company>
<vatno>12345678</vatno>
<name>
<founded>2013-12-31</founded>
<text>XYZ Inc</text>
</name>
<location>
<streetname>West Road</streetname>
<county>
<no>12345</no>
<text>East County</text>
<county>
</location>
</company>
</companies>
I am trying to get specific info from the elements into PHP variables.
To get "vatno" I use:
$vatno = $xmlObject->item($i)->getElementsByTagName('vatno')->item(0)->childNodes->item(0)->nodeValue;
But what if I need the county name for example?
I cannot use getElementsByTagName('text') as it would get the company name also using the element name "text".
You may be better off using SimpleXML, you can then access the various components in a more intuitive way.
The example above would be something like...
$data = <<< XML
<companies>
<company>
<vatno>12345678</vatno>
<name>
<founded>2013-12-31</founded>
<text>XYZ Inc</text>
</name>
<location>
<streetname>West Road</streetname>
<county>
<no>12345</no>
<text>East County</text>
</county>
</location>
</company>
</companies>
XML;
$xml = simplexml_load_string($data);
foreach ( $xml->company as $company ) {
echo $company->vatno.PHP_EOL;
echo $company->location->county->text.PHP_EOL;
}
So each sub element is accessed using ->.
If you wanted to stick with what you already had, you should be able to use...
$countyName = $xmlObject->item($i)->getElementsByTagName('text')->item(1)
->nodeValue;
Using item(1) will fetch the second instance of the <text> elements, so this assumes that the name will have this value as well.
It works with SimpleXML if I use
$xml = simplexml_load_string($data);
foreach ( $xml->companies->company as $company ) {
echo $company->vatno.PHP_EOL;
echo $company->location->county->text.PHP_EOL;
}
Introduction
I have some codes in SQL. I go code by code in while loop in php and search these codes in XML feed.
Programm code
I have the following programm code
$x_search = $xml->xpath("//Item[#Sort='$sort']");
if(!$x_search){
$x_Id = $x_search[0]->attributes()->Id;
echo $sort." - ".$x_Id."<BR />";
}
Problem
It is possible, that some code is not in SQL. So I get this error message:
Undefined offset: 0 in
How to do something like if you find it in XML, $x_Id = $x_search[0]->attributes()->Id;?
I have tried already:
$x_search = $xml->xpath("//*[#Sort='$sort']");
if(!empty($x_search)){
if(isset($x_search)){
Example XML:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item Id="12860" IdP="-2147483648" Sort="0001KC" Name="Computers">
<StoItem />
</Item>
</Root>
Examples for $sort:
00004M
12860
12859
12859
12861
12861
12862
12862
12863
12863
12864
Thank you
SimpleXMLElement::xpath() always returns an array of SimpleXMLElement objects. The array is empty, if nothing is matched. The result can equal false, if the expression is invalid (programming error). So if (!empty($x_search)) ... or if ($x_search) ... can be used as condition to check the result. false is an empty value and an empty array equals false. Both conditions will only be true if the result from the expression is an array with at least a single element.
$xmlString = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item Id="12860" IdP="-2147483648" Sort="0001KC" Name="Computers">
<StoItem />
</Item>
</Root>
XML;
$xml = new SimpleXmlElement($xmlString);
$sort = '0001KC';
$x_search = $xml->xpath("//Item[#Sort='$sort']");
if (!empty($x_search)) {
$x_Id = $x_search[0]->attributes()->Id;
echo $sort." - ".$x_Id."<BR />";
}
Output: https://eval.in/406640
0001KC - 12860<BR />
Most of your example values for $sort look like Id attribute values. The second one, is the Id attribute value in the example XML.
If you want to match the Id attribute, the Xpath expression would be:
//Item[#Id='$sort']
It is even possible to match both attributes:
//Item[#Id='$sort' or #Sort='$sort']
This is an example of the XML. A I wrote it is possible, that some code is not in SQL. So that's why I need to solve this problem. Something like write me an echo only if you find this sort in XML.
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item Id="12860" IdP="-2147483648" Sort="0001KC" Name="Computers">
<StoItem />
</Item>
</Root>
The begin of the list of codes that enter invariable $sort
00004M
12860
12859
12859
12861
12861
12862
12862
12863
12863
12864
I have the following XML documment:
<list>
<person>
<name>Simple name</name>
</person>
</list>
I try to read it, and basically create another "person" element. The output I want to achieve is:
<list>
<person>
<name>Simple name</name>
</person>
<person>
<name>Simple name again</name>
</person>
</list>
Here is how I am doing it:
$xml = new DOMDocument();
$xml->load('../test.xml');
$list = $xml->getElementsByTagName('list') ;
if ($list->length > 0) {
$person = $xml->createElement("person");
$name = $xml->createElement("name");
$name->nodeValue = 'Simple name again';
$person->appendChild($name);
$list->appendChild($person);
}
$xml->save("../test.xml");
What I am missing here?
Edit: I have translated the tags, so that example would be clearer.
Currently, you're pointing/appending to the node list instead of that found parent node:
$list->appendChild($person);
// ^ DOMNodeList
You should point to the element:
$list->item(0)->appendChild($person);
Sidenote: The text can already put inside the second argument of ->createElement():
$name = $xml->createElement("name", 'Simple name again');
I was tesing with a simple example of how to display XML in browser using PHP and found this example which works good
<?php
$xml = new DOMDocument("1.0");
$root = $xml->createElement("data");
$xml->appendChild($root);
$id = $xml->createElement("id");
$idText = $xml->createTextNode('1');
$id->appendChild($idText);
$title = $xml->createElement("title");
$titleText = $xml->createTextNode('Valid');
$title->appendChild($titleText);
$book = $xml->createElement("book");
$book->appendChild($id);
$book->appendChild($title);
$root->appendChild($book);
$xml->formatOutput = true;
echo "<xmp>". $xml->saveXML() ."</xmp>";
$xml->save("mybooks.xml") or die("Error");
?>
It produces the following output:
<?xml version="1.0"?>
<data>
<book>
<id>1</id>
<title>Valid</title>
</book>
</data>
Now I have got two questions regarding how the output should look like.
The first line in the xml file '', should not be displayed, that is it should be hidden
How can I display the TextNode in the next line. In total I am exepecting an output in this fashion
<data>
<book>
<id>1</id>
<title>
Valid
</title>
</book>
</data>
Is that possible to get the desired output, if so how can I accomplish that.
Thanks
To skip the XML declaration you can use the result of saveXML on the root node:
$xml_content = $xml->saveXML($root);
file_put_contents("mybooks.xml", $xml_content) or die("cannot save XML");
Please note that saveXML(node) has a different output from saveXML().
First question:
here is my post where all usable threads with answers are listed: How do you exclude the XML prolog from output?
Second question:
I don't know of any PHP function that outputs text nodes like that.
You could:
read xml using DomDocument and save each node as string
iterate trough nodes
detect text nodes and add new lines to xml string manually
At the end you would have the same XML with text node values in new line:
<node>
some text data
</node>