I have some data I converted to XML from a KML file and I was curious how to use PHP or Ruby to get back things like the neighborhood names and coordinates. I know when they have a tag around them like so.
<cities>
<neighborhood>Gotham</neighborhood>
</cities>
but the data is unfortunately formatted as:
<SimpleData name="neighborhd">Colgate Center</SimpleData>
instead of
<neighborhd>Colgate Center</neighborhd>
This is the KML source:
How can I use PHP or Ruby to pull data from something like this? I installed some Ruby gems for parsing XML data but XML is just something I haven't worked with much.
Your XML is invalid, but Nokogiri will attempt to fix it up.
Here's how to check for invalid XML/XHTML/HTML and how to rewrite the section you want.
Here's the setup:
require 'nokogiri'
doc = Nokogiri.XML(<<EOT)
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://earth.google.com/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<Schema name="Sample_Neighborhoods_Samples" id="Sample_Neighborhoods_Samples">
<SimpleField type="int" name="nid"/>
<SimpleField type="string" name="neighborhd"/>
<SimpleField type="string" name="place"/>
<SimpleField type="string" name="placecode"/>
<SimpleField type="string" name="nbr_type"/>
<SimpleField type="string" name="po_name"/>
<SimpleField type="string" name="metro"/>
<SimpleField type="string" name="country"/>
<SimpleField type="string" name="state"/>
<SimpleField type="string" name="statefips"/>
<SimpleField type="string" name="county"/>
<SimpleField type="string" name="countyfips"/>
<SimpleField type="string" name="mcd"/>
<SimpleField type="string" name="mcdfips"/>
<SimpleField type="string" name="cbsa"/>
<SimpleField type="string" name="cbsacode"/>
<SimpleField type="string" name="cbsatype"/>
<SimpleField type="double" name="cenlat"/>
<SimpleField type="double" name="cenlon"/>
<SimpleField type="int" name="color"/>
<SimpleField type="string" name="ncs_code"/>
<SimpleField type="string" name="release"/>
</Schema>
<Style id="KMLSTYLER_6">
<LabelStyle>
<scale>1.0</scale>
</LabelStyle>
<LineStyle>
<colorMode>normal</colorMode>
</LineStyle>
<PolyStyle>
<color>7f4080ff</color>
<colorMode>random</colorMode>
</PolyStyle>
</Style>
<name>Sample_Neighborhoods_NYC</name>
<visibility>1</visibility>
<Folder id="kml_ft_Sample_Neighborhoods_Samples">
<name>Sample_Neighborhoods_Samples</name>
<Folder id="kml_ft_Sample_Neighborhoods_Samples_Sample_Neighborhoods_NYC">
<name>Sample_Neighborhoods_NYC</name>
<Placemark id="kml_1">
<name>Colgate Center</name>
<Snippet> </Snippet>
<styleUrl>#KMLSTYLER_6</styleUrl>
<ExtendedData>
<SchemaData schemaUrl="#Sample_Neighborhoods_Samples">
<SimpleData name="nid">7086</SimpleData>
<SimpleData name="neighborhd">Colgate Center</SimpleData>
<SimpleData name="place">Jersey City</SimpleData>
<SimpleData name="placecode">36000</SimpleData>
<SimpleData name="nbr_type">S</SimpleData>
<SimpleData name="po_name">JERSEY CITY</SimpleData>
<SimpleData name="metro">New York City, NY</SimpleData>
<SimpleData name="country">USA</SimpleData>
<SimpleData name="state">NJ</SimpleData>
<SimpleData name="statefips">34</SimpleData>
<SimpleData name="county">Hudson</SimpleData>
<SimpleData name="countyfips">34017</SimpleData>
<SimpleData name="mcd">Jersey City</SimpleData>
<SimpleData name="mcdfips">36000</SimpleData>
<SimpleData name="cbsa">New York-Northern New Jersey-Long Island, NY-NJ-PA</SimpleData>
<SimpleData name="cbsacode">35620</SimpleData>
<SimpleData name="cbsatype">Metro</SimpleData>
<SimpleData name="cenlat">40.7145135000001</SimpleData>
<SimpleData name="cenlon">-74.0343385</SimpleData>
<SimpleData name="color">1</SimpleData>
<SimpleData name="ncs_code">40910000</SimpleData>
<SimpleData name="release">1.12.2</SimpleData>
</SchemaData>
</ExtendedData>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>-74.036628,40.712211,0 -74.0357779999999,40.7120810000001,0 -74.035535,40.7122010000001,0 -74.0348299999999,40.71209,0 -74.034903,40.711804,0 -74.033761,40.7116560000001,0 -74.0334089999999,40.7121090000001,0 -74.032996,40.7141330000001,0 -74.0331899999999,40.7141790000001,0 -74.032656,40.7162500000001,0 -74.032231,40.716194,0 -74.032049,40.716908,0 -74.033871,40.7170370000001,0 -74.035629,40.7173710000001,0 -74.035669,40.7171650000001,0 -74.036009,40.715335,0 -74.036325,40.713625,0 -74.036482,40.7123580000001,0 -74.036628,40.712211,0 </coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
<Placemark id="kml_2">
<name>Colgate Center</name>
<Snippet> </Snippet>
<ExtendedData>
EOT
Here's how to see if there are errors. Any time errors is not empty you have a problem.
puts doc.errors
Here's one way to find the SimpleData nodes throughout a document. I prefer to use CSS accessors over XPath for readability reasons. Sometimes XPath is better because it allows better granularity when searching. You need to learn them both.
doc.search('ExtendedData SimpleData').each do |simple_data|
node_name = simple_data['name']
puts "<%s>%s</%s>" % [node_name, simple_data.text.strip, node_name]
end
Here's the output after running:
Premature end of data in tag ExtendedData line 87
Premature end of data in tag Placemark line 84
Premature end of data in tag Folder line 44
Premature end of data in tag Folder line 42
Premature end of data in tag Document line 3
Premature end of data in tag kml line 2
<nid>7086</nid>
<neighborhd>Colgate Center</neighborhd>
<place>Jersey City</place>
<placecode>36000</placecode>
<nbr_type>S</nbr_type>
<po_name>JERSEY CITY</po_name>
<metro>New York City, NY</metro>
<country>USA</country>
<state>NJ</state>
<statefips>34</statefips>
<county>Hudson</county>
<countyfips>34017</countyfips>
<mcd>Jersey City</mcd>
<mcdfips>36000</mcdfips>
<cbsa>New York-Northern New Jersey-Long Island, NY-NJ-PA</cbsa>
<cbsacode>35620</cbsacode>
<cbsatype>Metro</cbsatype>
<cenlat>40.7145135000001</cenlat>
<cenlon>-74.0343385</cenlon>
<color>1</color>
<ncs_code>40910000</ncs_code>
<release>1.12.2</release>
I'm not trying to modify the DOM, but it's easy to do:
doc.search('ExtendedData SimpleData').each do |simple_data|
node_name = simple_data['name']
simple_data.replace("<%s>%s</%s>" % [node_name, simple_data.text.strip, node_name])
end
puts doc.to_xml
After running this is the affected section:
<ExtendedData>
<SchemaData schemaUrl="#Sample_Neighborhoods_Samples">
<nid>7086</nid>
<neighborhd>Colgate Center</neighborhd>
<place>Jersey City</place>
<placecode>36000</placecode>
<nbr_type>S</nbr_type>
<po_name>JERSEY CITY</po_name>
<metro>New York City, NY</metro>
<country>USA</country>
<state>NJ</state>
<statefips>34</statefips>
<county>Hudson</county>
<countyfips>34017</countyfips>
<mcd>Jersey City</mcd>
<mcdfips>36000</mcdfips>
<cbsa>New York-Northern New Jersey-Long Island, NY-NJ-PA</cbsa>
<cbsacode>35620</cbsacode>
<cbsatype>Metro</cbsatype>
<cenlat>40.7145135000001</cenlat>
<cenlon>-74.0343385</cenlon>
<color>1</color>
<ncs_code>40910000</ncs_code>
<release>1.12.2</release>
</SchemaData>
</ExtendedData>
Related
I have gone through a lot of the SimpleXML questions on this site. My data is a bit strange and I cannot change that. I am trying to get things like 'Building1' and 'Hostname1' from my data, so I can take that data and look up other data, then display it.
Here is a sample of my data:
<?xml version='1.0' encoding='UTF-8'?>
<results preview = '0'>
<result offset='0'>
<field k='hostname'>
<value h='1'><text>Hostname 1</text></value>
</field>
<field k='os'>
<value><text>Windows 7</text></value>
</field>
<field k='location'>
<value h='1'><text>Building 1</text></value>
<field>
</result>
<result offset='1'>
<field k='hostname'>
<value h='1'><text>Hostname 2</text></value>
</field>
<field k='os'>
<value><text>Windows 10</text></value>
</field>
<field k='location'>
<value h='1'><text>Building 2</text></value>
</field>
</result>
........
And here is how I am trying to look at it:
$xml = simplexml_load_file(data.xml);
print_r($xml);
$testArray = new SimpleXMLElement($xml);
$records = $testArray->results->result;
print_r($records);
For some reason I just cannot figure out how to get the data from the xml elements. If anyone can point me in the right direction, I'd appreciate it. I've tried many, many options. Thanks-
This is a really common mistake, but a really hard one to spot if you don't know what you're looking for: the first object you get back when parsing with XML is the root element, not something representing the document.
So in your case, $testArray is the element <results preview = '0'>, and you want $testArray->result not $testArray->results->result.
By the way, "testArray" is a bad name for this variable - it's not an array, it's an object.
I used xml as string in file
<?php
$sXmlString = <<<EOF
<?xml version="1.0" encoding="UTF-8"?>
<results preview = "0">
<result offset="0">
<field k="hostname">
<value h="1"><text>Hostname 1</text></value>
</field>
<field k="os">
<value><text>Windows 7</text></value>
</field>
<field k="location">
<value h="1"><text>Building 1</text></value>
</field>
</result>
<result offset="1">
<field k="hostname">
<value h="1"><text>Hostname 2</text></value>
</field>
<field k="os">
<value><text>Windows 10</text></value>
</field>
<field k="location">
<value h="1"><text>Building 2</text></value>
</field>
</result>
</results>
EOF;
echo '<pre>';
$xml = simplexml_load_string($sXmlString);
print_r($xml);
echo '<hr/>';
echo count($xml->result);
echo '<hr/>';
foreach($xml->result as $report)
{
var_dump($report);
echo '<hr/>';
}
In the code you can see $xml it self reference to the "results" (or root) element.
You need to travel from the root to child elements. $xml->result will give the result object in the results set and you need to go for loop as it as array of objects.
I have a .kml file shaped like this :
<?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" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">
<Document>
<name>myFile.shp</name>
<Style id="style1">
<PolyStyle>
<color>ff00ff00</color>
</PolyStyle>
</Style>
<Folder id="layer 0">
<name>background</name>
<Placemark>
<styleUrl>#style1</styleUrl>
<LineString>
<coordinates>
-2.94040373,54.83409343483 -2.943834733,54.893839393
</coordinates>
</LineString>
</Placemark>
</Folder>
</Document>
</kml>
Question
How can I get this file as a DOMDocument, and get ALL tag element with name "coordinates" ?
The goal is to be able to get the coordinates, even if the file shape change, like for example :
<kml xmlns="http://earth.google.com/kml/2.0">
<Folder>
<name>OpenLayers export</name>
<description>No description available</description>
<Placemark>
<name>OpenLayers.Feature.Vector_7341</name>
<description>No description available</description>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>
-2.94040373,54.83409343483 -2.943834733,54.893839393
</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</Placemark>
</Folder>
</kml>
My attempts was to loop through the document using simplexml_load_file() but unfortunately I would not be reliable as the "tag order" change between those 2 documents, and I do not know why it does not follow a single pattern (which lead me to ask this question because it may have more than 2 shape for a KML ? correct me if I am wrong).
Use DOMDocument class to parse XML. Then use getElementsByTagName() to get all coordinates elements.
$dom = new DOMDocument();
// load file
$dom->load("file.kml");
// get coordinates tag
$coordinates = $dom->getElementsByTagName("coordinates");
foreach($coordinates as $coordinate){
echo $coordinate->nodeValue;
}
I have the following XML file:
<XmlSports xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" CreateDate="2014-11-17T14:46:38.6954+01:00">
<Sport Name="SOCCER" ID="7">
<Event Name="African Nations Cup" ID="54" IsLive="false">
<Match Name="DR Congo v Sierra Leone" ID="1552163" StartDate="2014-11-19T15:00:00.1+01:00">
<Bet Name="Double Chance" ID="6337125"/>
<Bet Name="Draw No Bet" ID="6337124"/>
<Bet Name="Match Odds" ID="6337119"/>
<Bet Name="Odds or Evens" ID="6337123"/>
<Bet Name="Over/Under" ID="6337121"/>
</Match>
</Event>
</Sport>
I am just including a small portion of the file but basically this file includes other sports aside from just soccer, separated by 'Sport Name="NAME OF SPORT" ID="ID OF SPORT"'.
I want to extract just the SOCCER node. I have tried using XPath but have not succeeded.
How can I extract just the 'Sport Name="SOCCER" ID="7"' part out of this XML?
This should give you a starting point.
Assuming that $string contains your valid xml.
$xml = simplexml_load_string($string);
$sport = $xml->xpath("//Sport[#Name='SOCCER']");
print_r($sport);
I wish you have tried this one
//XmlSports/Sport[#Name='SOCCER' and #ID='7']
I use the folowing PHP to extract the coordinates from a kml file:
$xml = simplexml_load_file('data.kml');
$data = $xml->Document->Folder->Placemark;
foreach ($data as $record) {
$coordinates = $record->MultiGeometry->Polygon->outerBoundaryIs->LinearRing->coordinates[0];
}
This works fine for the coordinates. But how can I Extact the ExtendedData?
The kml file I am using:
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:atom="http://www.w3.org/2005/Atom" xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>pc4_single_vlak</name>
<visibility>1</visibility>
<Schema name="pc4_single_vlak" id="kml_schema_ft_pc4_single_vlak">
<SimpleField type="xsd:string" name="pc4">
<displayName>pc4</displayName>
</SimpleField>
<SimpleField type="xsd:string" name="woonplaats">
<displayName>woonplaats</displayName>
</SimpleField>
<SimpleField type="xsd:string" name="wplnaam_uniek">
<displayName>wplnaam_uniek</displayName>
</SimpleField>
<SimpleField type="xsd:string" name="gemeente">
<displayName>gemeente</displayName>
</SimpleField>
<SimpleField type="xsd:string" name="provincie">
<displayName>provincie</displayName>
</SimpleField>
</Schema>
<Folder id="kml_ft_pc4_single_vlak">
<name>pc4_single_vlak</name>
<Placemark id="kml_1">
<name>kml_1</name>
<snippet></snippet>
<ExtendedData>
<SchemaData schemaUrl="#kml_schema_ft_pc4_single_vlak">
<SimpleData name="pc4">4001</SimpleData>
<SimpleData name="woonplaats">Tiel</SimpleData>
<SimpleData name="wplnaam_uniek">Tiel</SimpleData>
<SimpleData name="gemeente">Tiel</SimpleData>
<SimpleData name="provincie">Gelderland</SimpleData>
</SchemaData>
</ExtendedData>
<MultiGeometry>
<Polygon>
<outerBoundaryIs>
<LinearRing>
<coordinates>...</coordinates>
</LinearRing>
</outerBoundaryIs>
</Polygon>
</MultiGeometry>
</Placemark>
</Folder>
</Document>
</kml>
I tried all kinds of things, but I cant get it to work.
I'm not sure where the hiccup is, but hopefully this provides some clarity.
Let's get the ExtendedData parent:
echo $record->ExtendedData;
No attributes though, only children, let's get the schemaUrl from the first SchemaData child:
echo $record->ExtendedData->SchemaData[0]['schemaUrl'];
When getting attrributes, you access them as an index of the element.
Here's a working example
I found the solution to my own question :). I gues this isnt the best way to do it. But I used the folowing code to get to the different SimpleData fields:
echo $record->ExtendedData->SchemaData->SimpleData[0];
echo $record->ExtendedData->SchemaData->SimpleData[1];
echo $record->ExtendedData->SchemaData->SimpleData[2];
enz..
Again. I am sure there is a better solution to this. If someone knows, please comment!
I am using shopify webhook to update my sql server's 'qty' field when order updated,
below is my php code
?php
$xmlData = fopen('php://input' , 'rb');
while (!feof($xmlData)) { $xmlString .= fread($xmlData, 4096); }
fclose($xmlData);
$xml = new SimplexmlElement($xmlString);
file_put_contents('orders/order' . '.xml', $xmlString);
$dom = new DomDocument();
$dom->load('orders/order.xml');
$itemList = $dom->getElementsByTagName('line-item');
foreach($itemList as $item) {
$sku='';
$qty='';
foreach($item->childNodes as $child) {
if ($child->localName == 'sku') {
$sku = $child->textContent;
}
if ($child->localName == 'quantity') {
$qty = $child->textContent;
}
}
mysql_connect ("?????????????????????????");
mysql_select_db("??????????");
$query = "UPDATE xcart_categories SET product_count = product_count - $qty WHERE description='$sku';";
mysql_query($query);
}
and below is xml file i am getting from shopify webhook
<?xml version="1.0" encoding="UTF-8"?>
<order>
<buyer-accepts-marketing type="boolean">true</buyer-accepts-marketing>
<closed-at type="datetime" nil="true"></closed-at>
<currency>USD</currency>
<email>yeongju_l#yahoo.com</email>
<financial-status>pending</financial-status>
<fulfillment-status>fulfilled</fulfillment-status>
<gateway>Local Pick-Up</gateway>
<id type="integer">140303247</id>
<name>#1012</name>
<note></note>
<number type="integer">12</number>
<subtotal-price type="decimal">0.2</subtotal-price>
<taxes-included type="boolean">false</taxes-included>
<total-discounts type="decimal">0.0</total-discounts>
<total-line-items-price type="decimal">0.2</total-line-items-price>
<total-price type="decimal">0.2</total-price>
<total-price-usd type="decimal">0.2</total-price-usd>
<total-tax type="decimal">0.0</total-tax>
<total-weight type="integer">0</total-weight>
<updated-at type="datetime">2012-09-16T21:20:07-04:00</updated-at>
<created-at type="datetime">2012-09-16T21:08:30-04:00</created-at>
<token>dcf523d93c68159c15a7c8d1fabbee07</token>
<landing-site>/products/test</landing-site>
<referring-site></referring-site>
<cancelled-at type="datetime" nil="true"></cancelled-at>
<cancel-reason nil="true"></cancel-reason>
<cart-token>a9a7bc5d8103f6a3bb45e827f0cb8928</cart-token>
<browser-ip nil="true"></browser-ip>
<landing-site-ref nil="true"></landing-site-ref>
<order-number type="integer">1012</order-number>
<discount-codes type="array"/>
<note-attributes type="array">
</note-attributes>
<processing-method>manual</processing-method>
<line-items type="array">
<line-item>
<id type="integer">228531213</id>
<requires-shipping type="boolean">false</requires-shipping>
<fulfillment-service>manual</fulfillment-service>
<grams type="integer">0</grams>
<price type="decimal">0.2</price>
<quantity type="integer">1</quantity>
<sku>1234567</sku>
<title>test</title>
<product-id type="integer">104663831</product-id>
<variant-id type="integer">240660979</variant-id>
<vendor>5 Second</vendor>
<variant-title nil="true"></variant-title>
<fulfillment-status>fulfilled</fulfillment-status>
<name>test</name>
<variant-inventory-management></variant-inventory-management>
<properties type="array">
</properties>
</line-item>
</line-items>
<shipping-lines type="array"/>
<tax-lines type="array">
<tax-line>
<title>NY State Tax</title>
<price type="decimal">0.0</price>
<rate type="float">0.04</rate>
</tax-line>
<tax-line>
<title>Queens County Tax</title>
<price type="decimal">0.0</price>
<rate type="float">0.04875</rate>
</tax-line>
</tax-lines>
<billing-address>
<first-name>Yeongju</first-name>
<last-name>Lee</last-name>
<address1>14809 northern blvd</address1>
<address2></address2>
<city>Flushing</city>
<company></company>
<country>United States</country>
<phone></phone>
<province>New York</province>
<zip>11354</zip>
<latitude type="decimal">40.76529</latitude>
<longitude type="decimal">-73.81831</longitude>
<name>Yeongju Lee</name>
<country-code>US</country-code>
<province-code>NY</province-code>
</billing-address>
<fulfillments type="array">
<fulfillment>
<id type="integer">67712419</id>
<order-id type="integer">140303247</order-id>
<created-at type="datetime">2012-09-16T21:20:07-04:00</created-at>
<updated-at type="datetime">2012-09-16T21:20:07-04:00</updated-at>
<tracking-number nil="true"></tracking-number>
<tracking-company nil="true"></tracking-company>
<status>success</status>
<service>manual</service>
<tracking-url>http://www.google.com/search?q=</tracking-url>
<receipt>
</receipt>
<line-items type="array">
<line-item>
<id type="integer">228531213</id>
<requires-shipping type="boolean">false</requires-shipping>
<fulfillment-service>manual</fulfillment-service>
<grams type="integer">0</grams>
<price type="decimal">0.2</price>
<quantity type="integer">1</quantity>
<sku>1234567</sku>
<title>test</title>
<product-id type="integer">104663831</product-id>
<variant-id type="integer">240660979</variant-id>
<vendor>5 Second</vendor>
<variant-title nil="true"></variant-title>
<fulfillment-status>fulfilled</fulfillment-status>
<name>test</name>
<variant-inventory-management></variant-inventory-management>
<properties type="array">
</properties>
</line-item>
</line-items>
</fulfillment>
</fulfillments>
<customer>
<id type="integer">96489088</id>
<email>yeongju_l#yahoo.com</email>
<accepts-marketing type="boolean">true</accepts-marketing>
<first-name>Yeongju</first-name>
<last-name>Lee</last-name>
<orders-count type="integer">12</orders-count>
<total-spent type="decimal">16.26</total-spent>
<note nil="true"></note>
<created-at type="datetime">2012-08-17T11:31:50-04:00</created-at>
<updated-at type="datetime">2012-09-16T21:20:07-04:00</updated-at>
<state>enabled</state>
<last-order-id type="integer">140303509</last-order-id>
<tags>C</tags>
<last-order-name>#1013</last-order-name>
</customer>
</order>
like you see i got two sku and qty because of duplicated line-item so for example when customer order one "product name - test in this case i got "-2" quantity update in my sql server sku test field , but when i am using webhook event when order creation it worked i mean i see only one line item but all the other cases(when order updated, when order payment, when order fullfillment..) show me duplicated line item even there is only one item ordered
anyone who got idea to fix this thing?
Thank you so much in advance guys......
You're parsing your XML badly. You'll notice that one of the line-items tags is in the root of the response, while the other is nested under the fulfillments node. The former is a full listing of the line items for the order, and the latter is a record of the individual fulfillments made against the order (there might be several).
I'd find an XML parser and put it to work on the data so that you can access it in a structured fashion and only pull line items from the first line-items node.