I need to get the value of every "custom_field" named "ZIP" out of the following XML-file using PHP.
When I parse it, I always get either the values of all projects or an empty array?
Can somebody help?
<?xml version="1.0" encoding="UTF-8"?>
<projects total_count="237" offset="0" limit="100" type="array">
<project>
<id>239</id>
<name>ABC</name>
<identifier></identifier>
<description></description>
<status>1</status>
<is_public>false</is_public>
<custom_fields type="array">
<custom_field id="18" name="Name affix">
<value></value>
</custom_field>
<custom_field id="20" name="ZIP">
<value>X1111</value>
</custom_field>
</custom_fields>
<created_on>2017-06-05T16:33:13Z</created_on>
<updated_on>2017-06-19T13:46:08Z</updated_on>
</project>
<project>
<id>240</id>
<name>DEF</name>
<identifier></identifier>
<description></description>
<status>1</status>
<is_public>false</is_public>
<custom_fields type="array">
<custom_field id="18" name="Name affix">
<value></value>
</custom_field>
<custom_field id="20" name="ZIP">
<value>Y2222</value>
</custom_field>
</custom_fields>
<created_on>2017-06-05T16:33:14Z</created_on>
<updated_on>2017-06-05T16:33:14Z</updated_on>
</project>
...
I tried the following and get empty arrays:
$projects = simplexml_load_file($rm_host."/projects.xml?key=".$rm_sa_key."&limit=100");
foreach($projects->project as $project){
$zip = $project->xpath('//custom_field[#name="ZIP"]');
print_r($zip);
echo "<br/>";
}
When I try to replace the string with the following, it returns the value of all items, not of the specific one:
zip = $project->xpath('//custom_fields[#type="array"]/custom_field[#name="ZIP"]')
Finally worked after trying and trying.
The correct string to get the specific value was:
$zip = $project->custom_fields->xpath('custom_field[#name="ZIP"]/value')[0];
Without the slash in front of the xpath.
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'm very new to php so go easy on me.
I'm trying to search through a KML file for latitude and longitude values, then replace them with user inputted lat/long values. The problem I'm having is actually searching through the KML file to find the specific lat/long values to be replaced.
KML:
<?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">
<Folder>
<name>Folder-Name</name>
<open>1</open>
<gx:Tour>
<name class="name">Tour-Name</name>
<gx:Playlist>
<gx:FlyTo>
<LookAt>
<gx:horizFov>100</gx:horizFov>
<longitude class="lookat-long">33.33333</longitude>
<latitude class="lookat-lat">-111.11111</latitude>
<altitude>0</altitude>
<heading>0</heading>
<tilt>60</tilt>
<range>100</range>
<altitudeMode>relativeToGround</altitudeMode>
</LookAt>
</gx:FlyTo>
</gx:Playlist>
</gx:Tour>
</Folder>
</kml>
I need to replace "33.33333" and "-111.11111" with user inputted values. I have tried using SimpleXML, but it does not recognize the gx: part of the tag, since that is KML specific, and not part of XML. So when I try this code:
<?php
$xml = simplexml_load_file('my_kml_file');
print_r($xml)
?>
I get this output:
SimpleXMLElement Object ( [Folder] => SimpleXMLElement Object ( [name] => Temporary Places [open] => 0 ) )
It just stops at <open> because it doesn't recognize the rest. I have spent hours upon hours trying to figure out how to best do this and I just can't. Please help.
You can use the xpath method to get the namespaced node like so:
$xml = simplexml_load_file('my_kml_file');
$xml->xpath('//gx:Tour/gx:Playlist/gx:FlyTo')[0]->LookAt->longitude = 'newValue';
$xml->xpath('//gx:Tour/gx:Playlist/gx:FlyTo')[0]->LookAt->latitude = 'newValue';
print_r($xml->asXml());
Output:
<?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">
<Folder>
<name>Folder-Name</name>
<open>1</open>
<gx:Tour>
<name class="name">Tour-Name</name>
<gx:Playlist>
<gx:FlyTo>
<LookAt>
<gx:horizFov>100</gx:horizFov>
<longitude class="lookat-long">newValue</longitude>
<latitude class="lookat-lat">newValue</latitude>
<altitude>0</altitude>
<heading>0</heading>
<tilt>60</tilt>
<range>100</range>
<altitudeMode>relativeToGround</altitudeMode>
</LookAt>
</gx:FlyTo>
</gx:Playlist>
</gx:Tour>
</Folder>
</kml>
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>
I've got my script worked out to grab information from the CIM:
$content =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" .
"<getCustomerProfileRequest xmlns=\"AnetApi/xml/v1/schema/AnetApiSchema.xsd\">" .
merchantAuthenticationBlock().
"<customerProfileId>$cid</customerProfileId>" .
"</getCustomerProfileRequest>";
$response = send_xml_request($content);
$parsedresponse = parse_api_response($response);
So how, now, do I write the returned value to a variable?
I've tried:
$customerPaymentProfileId = $parsedresponse->customerPaymentProfileId;
$customerShippingAddressId = $parsedresponse->customerShippingAddressId;
But this returns the variables empty. I feel like I'm missing something simple.
To see the structure of $parsedresponse do either print_r($parsedresponse) or var_dump($parsedresponse). From there you can see how the array is structured and get your values from there.
FYI, the payment profiles are in an array so you will need to loop through them to get their values. Assuming parsedresponse is the root XML node you can get them like this:
foreach ($parsedresponse->profile->paymentProfiles as $profile)
{
echo $profile->customerPaymentProfileId;
}
FYI, this is a sample array structure for this response (From the AuthnetXML Sample Code. I am the author of this library):
<?xml version="1.0" encoding="utf-8"?>
<getCustomerProfileResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="AnetApi/xml/v1/schema/AnetApiSchema.xsd">
<messages>
<resultCode>Ok</resultCode>
<message>
<code>I00001</code>
<text>Successful.</text>
</message>
</messages>
<profile>
<merchantCustomerId>12345</merchantCustomerId>
<email>user#example.com</email>
<customerProfileId>5427896</customerProfileId>
<paymentProfiles>
<billTo>
<firstName>John</firstName>
<lastName>Smith</lastName>
<address>123 Main Street</address>
<city>Townsville</city>
<state>NJ</state>
<zip>12345</zip>
<phoneNumber>800-555-1234</phoneNumber>
</billTo>
<customerPaymentProfileId>4796541</customerPaymentProfileId>
<payment>
<creditCard>
<cardNumber>XXXX1111</cardNumber>
<expirationDate>XXXX</expirationDate>
</creditCard>
</payment>
</paymentProfiles>
<paymentProfiles>
<billTo>
<firstName>John</firstName>
<lastName>Doe</lastName>
<company/>
<address>123 Main St.</address>
<city>Bellevue</city>
<state>WA</state>
<zip>98004</zip>
<country>USA</country>
<phoneNumber>800-555-1234</phoneNumber>
<faxNumber>800-555-1234</faxNumber>
</billTo>
<customerPaymentProfileId>4796585</customerPaymentProfileId>
<payment>
<creditCard>
<cardNumber>XXXX1111</cardNumber>
<expirationDate>XXXX</expirationDate>
</creditCard>
</payment>
</paymentProfiles>
<shipToList>
<firstName>John</firstName>
<lastName>Smith</lastName>
<address>123 Main Street</address>
<city>Townsville</city>
<state>NJ</state>
<zip>12345</zip>
<phoneNumber>800-555-1234</phoneNumber>
<customerAddressId>4907537</customerAddressId>
</shipToList>
<shipToList>
<firstName>John</firstName>
<lastName>Doe</lastName>
<company/>
<address>123 Main St.</address>
<city>Bellevue</city>
<state>WA</state>
<zip>98004</zip>
<country>USA</country>
<phoneNumber>800-555-1234</phoneNumber>
<faxNumber>800-555-1234</faxNumber>
<customerAddressId>4907591</customerAddressId>
</shipToList>
</profile>
</getCustomerProfileResponse>