I Have a Parts List Coming from an XML File, It goes something like what is below. I Want to be able to generate using PHP a Combined List. For Example the "Part 1" Is recorded twice. I want the Qty to Show 13 Under Part 1 when Generated either in a JSON output or another XML Output. What would be the best way of doing that? I looked at the php function array_combine but wasn't able to figure out if the values could be combined mathematically instead of showing one of the results. I am loading the XML from a url in simplexml_load_file() function. Thank You for your help
My XML from URL:
<db>
<record>
<part>Part 1</part>
<qty>4</qty
</record>
<record>
<part>Part 2</part>
<qty>5</qty
</record>
<record>
<part>Part 1</part>
<qty>9</qty
</record>
</db>
Want to Display:
<db>
<record>
<part>Part 1</part>
<qty>13</qty>
</record>
<record>
<part>Part 2</part>
<qty>5</qty
</record>
</db>
Iterate over the XML document, accumulating part quantities as you go:
$simpleXmlElement = new SimpleXMLElement(<<<XML
<db>
<record>
<part>Part 1</part>
<qty>4</qty>
</record>
<record>
<part>Part 2</part>
<qty>5</qty>
</record>
<record>
<part>Part 1</part>
<qty>9</qty>
</record>
</db>
XML
);
$quantities = array();
foreach ($simpleXmlElement as $child) {
if ($child->part && $child->qty) {
$part = (string)$child->part;
if (!isset($quantities[$part])) {
$quantities[$part] = 0;
}
$quantities[$part] += (int)$child->qty;
}
}
echo json_encode($quantities);
simplexml_load_string() will parse the xml and build an array of all the records as 'record' => array(...)
you can then iterate the record array and group them by type
$parts = array();
foreach ($document['record'] as $item) {
if (isset($parts[$item['part']]) $parts[$item['part']] += $item['qty'];
else $parts[$item['part']] = $item['qty'];
}
after the above loop, $parts will contain a hash mapping the part name to qty
Related
An API returns the following, and I want to add another data record for testing purposes:
<?xml version="1.0" encoding="UTF-8" ?>
<API>
<code>0000</code>
<time>1549959757</time>
<data cnt="1">
<record>
<record>
<approval_idx>224833</approval_idx>
<reg_date>2019-02-12</reg_date>
<user_id>test</user_id>
<state1>10</state1>
<state2>03</state2>
<name>tester</name>
<e_id>3743</e_id>
<cname>test cname</cname>
<cId>3089</cId>
<loc>oh</loc>
</record>
</record>
</data>
</API>
I tried doing this
$utilApi = new libUtilApi($apiRequest);
$utilApi->request();
$resultAPI = $utilApi->response;
$resultAPI = simplexml_load_string($resultAPI);
$record = $resultAPI->addChild("record");
$record->addChild("approval_idx", "7546");
but the output look like this
<?xml version="1.0" encoding="UTF-8" ?>
<API>
<code>0000</code>
<time>1549959757</time>
<data cnt="1">
<record>
<record>
<approval_idx>224833</approval_idx>
<reg_date>2019-02-12</reg_date>
<user_id>test</user_id>
<state1>10</state1>
<state2>03</state2>
<name>tester</name>
<e_id>3743</e_id>
<cname>test cname</cname>
<cId>3089</cId>
<branch>oh</branch>
</record>
</record>
<record>
<approval_idx>7546</approval_idx>
<reg_date>2019-02-13</reg_date>
<user_id>test3</user_id>
<state1>75</state1>
<state2>09</state2>
<name>new test</name>
<e_id>7546</e_id>
<cname>Q1</cname>
<cId>325</cId>
<branch>oh</branch>
</record>
</data>
</API>
The added child is not going in right position, I tried
$record = $resultAPI->record[0]->addChild("record");
but it falls to an error like:
Call to a member function addChild() on null
-I just updated the code, yes the api is returning the proper format and data, its just that I can't make the addition of children on the right place
I have the following example XML document:
<?xml version="1.0" encoding="UTF-8" ?>
<database>
<record>
<id>1</id>
<a>5</a>
</record>
<record>
<id>5</id>
<a>8</a>
</record>
<record>
<id>7</id>
<!--No a record!-->
</record>
<record>
<id>6</id>
<a>10</a>
</record>
</database>
I want to iterate through each "record" element and fetch it's corresponding "a" element if it exists.
I attempted this using the following code:
$xml = new SimpleXMLElement($xmlStringDataFromFile);
foreach ($xml->record as $record) {
$id = $record->xpath("//id")[0];
$a = $record->xpath("//a")[0];
echo "{$id}: {$a}\n";
}
However the xpath performed is on the entire document. not on the individual "record" element. Thus I got the following output:
1: 5
1: 5
1: 5
1: 5
I want the following output:
1: 5
5: 8
7:
6: 10
How do I do this?
When you use // at the beginning of the XPath expression it will search from the document root and you always end up with the same result. Since the id and a elements are directly under record simply use
$id = $record->xpath("id")[0];
$a = $record->xpath("a")[0];
If they would be at same level below record start the XPath expression with a . to search relative to the context node:
$id = $record->xpath(".//id")[0];
$a = $record->xpath(".//a")[0];
There can be one or many similar XML tags inside a single XML record set. If there are many, I need them to be in a one tag, comma separated.
This is the XML I have now.
<?xml version="1.0"?>
<Results>
<Recordset setCount="3">
<Record setEntry="0">
<AU>One</AU>
<AU>Two</AU>
<AU>three</AU>
</Record>
<Record setEntry="1">
<AU>One</AU>
<AU>Two</AU>
<AU>Three</AU>
<AU>Four</AU>
<AU>Five</AU>
<AU>Six</AU>
<AU>Seven</AU>
</Record>
<Record setEntry="2">
<AU>One</AU>
</Record>
</Recordset>
</Results>
I need it to be like this. Please help with a code.
<?xml version="1.0"?>
<Results>
<Recordset setCount="3">
<Record setEntry="0">
<AU>One, Two, Three</AU>
</Record>
<Record setEntry="1">
<AU>One, Two, Three, Four, Five, Six, Seven</AU>
</Record>
<Record setEntry="2">
<AU>One</AU>
</Record>
</Recordset>
</Results>
This can be done with xpath. Here is an example with Simplexml:
You can first find all the first leafs:
foreach ($xml->xpath('//*[not(*) and not(preceding-sibling::*)]') as $firstLeaf) {
...
}
and then you concat the text together with all the following leafs:
$followingWithSameName = 'following-sibling::*[name(.) = name(preceding-sibling::*[last()])]';
// change the text of the first leaf
$firstLeaf[0] = implode(', ', $firstLeaf->xpath(".|$followingWithSameName"));
and then you remove all the following leafs:
// remove all following leafs with the same name
foreach ($firstLeaf->xpath($followingWithSameName) as $leaf) {
unset($leaf[0]);
}
Demo
This question already has an answer here:
How to concatenate similar tags in a XML file
(1 answer)
Closed 8 years ago.
Please help me with coding. #hakre
I have the following XML file.
<?xml version="1.0"?>
<Results>
<Recordset setCount="3">
<Record setEntry="0">
<TI>Test-1</TI>
<MO>Mo-1</MO>
<AU>One</AU>
<AU>Two</AU>
<AU>three</AU>
<JF>OK</JF>
<JT />
</Record>
<Record setEntry="1">
<TI>Test-2</TI>
<MO>Mo-2</MO>
<AU>One</AU>
<AU>Two</AU>
<AU>Three</AU>
<AU>Four</AU>
<AU>Five</AU>
<AU>Six</AU>
<AU>Seven</AU>
<JF />
<JT />
</Record>
<Record setEntry="2">
<TI>Test31</TI>
<MO>Mo-3</MO>
<AU>One</AU>
<AU />
<JF />
<JT />
</Record>
</Recordset>
</Results>
I want it to be like the following XML file.
<?xml version="1.0"?>
<Results>
<Recordset setCount="3">
<Record setEntry="0">
<TI>Test-1</TI>
<MO>Mo-1</MO>
<AU>One; Two; Tree</AU>
<JF>OK</JF>
<JT />
</Record>
<Record setEntry="1">
<TI>Test-2</TI>
<MO>Mo-2</MO>
<AU>One; Two; Three; Four; Five; Six; Seven</AU>
<JF />
<JT />
</Record>
<Record setEntry="2">
<TI>Test31</TI>
<MO>Mo-3</MO>
<AU>One</AU>
<JF />
<JT />
</Record>
</Recordset>
</Results>
These are the things I need to get done.
Concatenate values in SIMILAR XML tags into a one tag, separated by ";". ("AU tags" in the given example)
Leave the blank XML tags as they are.
PHP to create and save the second XML file by the name "SECOND.XML".
Here is one way to solve the problem. It doesn't hardcode any of the child node names (TI, MO, AU, etc.) so you could potentially use it on similar documents. I've put comments in the code so please read them and ask if you don't understand what the code is doing.
$txt = 'your XML string goes here';
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($txt);
$xp = new DOMXPath($dom);
# find all the nodes types that appear under Record
$cnode_type = array();
foreach ($xp->query("/Results/Recordset/Record/*") as $c) {
$cnode_type[] = $c->nodeName;
}
# cnode_type now contains the different child node types
$cnode_type = array_unique($cnode_type);
# get all the Record nodes
$recs = $xp->query("/Results/Recordset/Record");
# for every Record node...
foreach ($recs as $par) {
# for each type of child node...
foreach ($cnode_type as $c) {
# run an XPath query to count the number of children of node type $c
# if there are more than one, we need to remove the extras
if ($xp->evaluate("count($c)", $par) > 1) {
# go through all the $c nodes, saving the value in $node_vals
# delete the node
$node_vals = [];
foreach ($xp->query($c, $par) as $n) {
# only save the contents of nodes with a value
if (isset($n->nodeValue) && strlen($n->nodeValue) > 0) {
$node_vals[] = $n->nodeValue;
}
$par->removeChild($n);
}
# create a new $c node and set the value to the list in $node_vals
# add it to the parent node
$new_node = $dom->createElement($c, implode("; ", $node_vals));
$par->appendChild($new_node);
}
}
}
# print out the result
echo $dom->saveXML();
Output from the XML you have posted:
<?xml version="1.0"?>
<Results>
<Recordset setCount="3">
<Record setEntry="0">
<TI>Test-1</TI>
<MO>Mo-1</MO>
<JF>OK</JF>
<JT/>
<AU>One; Two; three</AU>
</Record>
<Record setEntry="1">
<TI>Test-2</TI>
<MO>Mo-2</MO>
<JF/>
<JT/>
<AU>One; Two; Three; Four; Five; Six; Seven</AU>
</Record>
<Record setEntry="2">
<TI>Test31</TI>
<MO>Mo-3</MO>
<JF/>
<JT/>
<AU>One</AU>
</Record>
</Recordset>
</Results>
I have this kind of XML:
<?xml version="1.0" encoding="utf-8"?>
<data>
<stats>
</stats>
<params>
</params>
<results>
<record id='SJDGH'>
<item>abc</item>
<item>def</item>
<item>ghi</item>
</record>
<record id='OIIO'>
<item>abc</item>
<item>def</item>
<item>ghi</item>
</record>
</results>
</data>
I'm generating a new <item> for every <record> in <results> in a loop:
// $data is SimpleXml objec from XML above
foreach ($data->results->record as $record)
{
$newitem = 'New item!'.time().$record->attributes()->id;
}
Somehow in this loop i need to change the SimpleXML object ($data) to contain new items in every <record>.
is it possible?
I needed a little guessing, but this might what you're looking for:
$records = $data->results->record;
foreach($records as $record)
{
$value = sprintf('New Item! %s / id:%s', time(), $record['id']);
$record->item[] = $value;
}
$data->asXML('php://output');
See it in action.
I think you might want to use addChild.
Check it out here: http://php.net/manual/en/simplexmlelement.addchild.php