-1

I am taking an xml file that looks like this

    <FCDMC><rpt_info created="data generated 04/16/2013  16:45"/><gage_rain id="770" last_rpt="2013-04-16T14:22:11" min_10="0.00" min_30="0.00" hour_1="0.00" hour_3="0.00" hour_6="0.00" day_1="0.00" day_3="0.00" day_7="0.00" name="Tat Momolikot Dam" lat="032:39:04" long="111:55:41"/></FCDMC>

Using this xsl style sheet to change/modify the xml document.

<xsl:stylesheet version="1.0">
  <xsl:output method="xml" encoding="utf-8" media-type="text/xml" indent="yes"/>
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="rpt_info">
    <xsl:element name="meta" select=".">
      <xsl:for-each select="@created">
        <xsl:element name="created" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
  <xsl:template match="gage_rain">
    <xsl:element name="data" select=".">
      <xsl:for-each select="@id">
        <xsl:element name="site" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@lat">
        <xsl:element name="latitude" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@long">
        <xsl:element name="longitude" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@name">
        <xsl:element name="name" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@last_rpt">
        <xsl:element name="last_rpt" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@min_10">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@min_30">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@hour_1">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@hour_3">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@hour_6">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@day_1">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@day_3">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
      <xsl:for-each select="@day_7">
        <xsl:element name="rain" select=".">
          <xsl:value-of select="."/>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Than I am using PHP to output the new xml file

<?php
header('Content-Type: application/xml');
$xml = new DOMDocument;
$xml->load('http://alert.fcd.maricopa.gov/alert/Google/xml/fcdmc_alert_rain.xml');
$xsl = new DOMDocument;
$xsl->load('http://alert.fcd.maricopa.gov/alert/Google/v3/xslt/fcdmc_alert_rain.xsl');
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); 
echo $proc->transformToXML($xml);
?>

and this php to output JSON

<?php
$xml = simplexml_load_file('http://alert.fcd.maricopa.gov/alert/Google/v3/php/rainfall_data.php');
$json = json_encode($xml);
echo $json;
?>

This is my current JSON output

{"meta":{"created":"04-18-2013 12:45"},"data":[{"site":"770","latitude":"032:39:04","longitude":"111:55:41","name":"Tat Momolikot Dam","last_rpt":"2013-04-18T11:22:11","rain":["0.00","0.00","0.00","0.00","0.00","0.00","0.00","0.00"]}]} 

This is what I need my JSON output to look like. I need to remove the double quotes ("")that are around the 0.00 values.

{"meta":{"created":"04-18-2013 12:45"},"data":[{"site":"770","latitude":"032:39:04","longitude":"111:55:41","name":"Tat Momolikot Dam","last_rpt":"2013-04-18T11:22:11","rain":[0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00]}]} 

How do I change the "rain":[string]?

Do I do it in xsl? In php? Thank you.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
MillerC
  • 33
  • 1
  • 9
  • you can simply use `str_replace('"0.00"', '0.00', $json)` –  Apr 18 '13 at 20:17
  • I don't think str_replace will work because the 0.00 values will change. These are values are rainfall amounts from rain gages. – MillerC Apr 18 '13 at 20:25
  • The provided XSLT code contains numerous syntax and semantic errors -- please, provide the code that you are *really* using. – Dimitre Novatchev Apr 19 '13 at 02:16
  • @dimitrenovatchev that is the code I am really using. This is my first time using xsl to transform an XML file so I can use php to output JSON. I need JSON data so I can use JavaScript and D3Loader to plot markers on my Google Map. What errors are in it? It seems to be outputting what I need. – MillerC Apr 19 '13 at 05:06
  • @MillerC, Your XSLT processor must have output the error messages -- just read them. You have document malformedness ( as XML) and also, an `xsl:element` cannot have a `select` attribute. – Dimitre Novatchev Apr 19 '13 at 05:13

3 Answers3

1

As of php 5.3.3 you can pass a JSON_NUMERIC_CHECK flag to json_encode that will check if a value is numeric and encode the json string with a number instead of a string.

Edit:

Per my last comment, using string replace, this would work:

<?php
//the json data, since I don't have the original data, I am just decoding the json output.
$json = '{"meta":{"created":"04-18-2013 12:45"},"data":[{"site":"770","latitude":"032:39:04","longitude":"111:55:41","name":"Tat Momolikot Dam","last_rpt":"2013-04-18T11:22:11","rain":["0.00","0.00","0.00","0.00","0.00","0.00","0.00","0.00"]}]}';

//decode the json output
$array = json_decode($json, 1);

//an empty array for rain data
$rain = array();

//loop through each data
foreach($array['data'] as $k=>$v){
    //save the rain data
    $rain[$k] = $v['rain'];
    //overwrite the rain data with a simple unique string that can be replaced
    $array['data'][$k]['rain'] = "{rain data {$k}}";
}

//encode the new data with the replacement string
$json = json_encode($array);

//loop over the rain data replacing the rain data replacement string with a JSON_NUMERIC_CHECK json_encoded rain data
foreach($rain as $k=>$v){
    //build the search string
    $search = '"{rain data '.$k.'}"';
    //build the replace string
    $replace = json_encode($v, JSON_NUMERIC_CHECK);
    //do the replace
    $json = str_replace($search, $replace, $json);
}
var_dump($json);

http://codepad.viper-7.com/hiWxjH

Jonathan Kuhn
  • 15,279
  • 3
  • 32
  • 43
  • JSON_NUMERIC_CHECK does do the job at removing the double quotes around the 0.00 values BUT it also removes the double quotes around my "site":"770". That is also a numeric value and I need quotes around that. – MillerC Apr 18 '13 at 20:27
  • @MillerC Then you would have to make your own JSON formatter. It's either all or nothing. However, javascript is loosely typed, you could just loop over all the rain values and multiply by 1 or parseInt to convert them to numeric on the client side after a JSON.parse. Or on the other side, use JSON_NUMERIC_CHECK and do a `''+site` to convert that to string. – Jonathan Kuhn Apr 18 '13 at 20:30
  • "and do a `''+site` to convert that to string." how would I do this in php? @jonathankuhn – MillerC Apr 18 '13 at 20:42
  • Doesn't matter, `JSON_NUMERIC_CHECK` essentially checks each value in is_numeric. Anything that matches a numeric format would be treated as a number in the resulting json string. To do it in php only, you would either have to make site a non-numeric string (put a character in it) or treat the rain numbers as a string. On the client side though, is there a reason why the rain numbers need to be a number and not a string? Javascript is loosely typed and so numbers will be implicitly converted to strings in any expression. – Jonathan Kuhn Apr 18 '13 at 21:26
  • @MillerC If you are willing to do a string replace, you could save the value under the "rain" key to another variable, replace it with a value to search for like "{rain data}", convert the simplexml object to json, convert the saved "rain" xml to json using `JSON_NUMERIC_CHECK` and do a `string_replace('"{rain data}"', $rainJSON, $json)`. – Jonathan Kuhn Apr 18 '13 at 21:51
  • That works! I changed the beginning to $array = json_decode(file_get_contents("http://alert.fcd.maricopa.gov/alert/Google/v3/php/currentrain.php"), 1); (that's my json file location). BUT when I run the php there is a string(45876)"" at the beginning and my decimal places where removed. See http://alert.fcd.maricopa.gov/alert/Google/v3/php/currain.php. Any ideas on why those two things happened? I really appreciate your help. Also see http://codepad.viper-7.com/CLrdN9 – MillerC Apr 18 '13 at 22:46
  • @MillerC that is because I am doing a var_dump of the json string instead of an echo. var_dump gives more data about a string such as length. I use it for debugging because there are some variable types that echo doesn't display. For example `echo "";` will just echo nothing, but `var_dump("")` will output something like `string(0) ""` so you can specifically see that it is a string type with length of 0. Just change the var_dump to an echo. – Jonathan Kuhn Apr 19 '13 at 07:27
  • Thank you for explaining the var_dump("") output. One last question. How do I get the decimal to display? The tenths and hundredths place needs to display. The output strips them down to [0,0,0,0,0,0,0,0] instead of [0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00]. I appreciate your time @jonathankuhn – MillerC Apr 19 '13 at 14:54
  • when it rains the values with change in the hundredths place 0.04, 0.08, 0.12... but would like 0.00 to show both decimal places cause my JavaScript uses return d.value.rain[dataSet].toFixed(2); to plot the rainfall dataSet. I am getting an error at toFixed(2) because there is not any decimal places. – MillerC Apr 19 '13 at 15:12
  • Are you sure that is the problem? I can run code like `(0).toFixed(2)` and get `0.00` as the result. Also, this whole bit of ugly code can be avoided if you just used json_encode without `JSON_NUMERIC_CHECK` and did `(d.value.rain[dataSet]*1).toFixed(2)` to coerce the string to be a number. – Jonathan Kuhn Apr 19 '13 at 15:19
  • I have been messing with this map all day. You were right that wasn’t the problem. After working through some other things and editing my xml tpt file, my map is displaying my markers. See (http://alert.fcd.maricopa.gov/alert/Google/v3/gmap.html). They are just all plotting in a straight line instead of in the right lat/long positions. I had to change the lat/long to decimal and they now have double quotes around them so when it reads my JavaScript (http://alert.fcd.maricopa.gov/alert/Google/v3/js/rainfall_v3.js) its not getting the longitude value. – MillerC Apr 19 '13 at 20:53
  • @MillerC Lattitude has spaces in front of the value. I see values like: `..."latitude":" 32.87950"...` – Jonathan Kuhn Apr 19 '13 at 21:38
  • I got rid of the spaces in front of the latitude values and had to edit the code a little bit for the lat/long and IT IS ALL WORKING!!!! (alert.fcd.maricopa.gov/alert/Google/v3/gmap.html). THANK YOU SO MUCH FOR ALL YOUR HELP!!!!! You are a total life saver :) @JonathanKuhn – MillerC Apr 19 '13 at 22:30
1

You json-encode a SimpleXMLElement which by default returns the element node values as strings.

If you want to change this behavior, you need to extend from it and change the way it encodes the object for json, e.g. the rain array (if it exists) should be converted to float values:

class JsonSerializeXMLElement extends SimpleXMLElement implements JsonSerializable
{
    public function jsonSerialize() {
        $array = (array) $this;
        if ($this->rain) {
            $array['rain'] = array_map('floatval', $array['rain']);
        }
        return $array;
    }
}

Your script then only needs the little change to hint the loading function to use the class with the changed serialization behavior:

$filename = 'http://alert.fcd.maricopa.gov/alert/Google/v3/php/rainfall_data.php';
$xml = simplexml_load_file($filename, 'JsonSerializeXMLElement');
$json = json_encode($xml);

And that's it already.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • Where exactly am I supposed to be adding the jsonSerialize function? I added it at the beginning of the script and that doesn’t work. @hakre – MillerC Apr 18 '13 at 23:08
  • @MillerC: See the demo: http://eval.in/16783 - For the details of the interface see: http://php.net/JsonSerializable – hakre Apr 18 '13 at 23:16
  • thank you for the demo. why does the site outputs have NaN in them? IE: "site":"770NaN" instead of "site":"770". @hakre – MillerC Apr 18 '13 at 23:39
  • @MillerC: Because that is in the XML already. Has nothing to do with my PHP script. Maybe the site where you fetch the XML from is broken (while testing it wasn't stable for me either so you might want to do some validation and caching) – hakre Apr 18 '13 at 23:43
  • How do I get the XML data from rainfall_data.php into the code by using the url. This file is constantly changing so I cant hardcode any specific values into the php. See http://eval.in/16794 - keep getting errors.@hakre Thanks! – MillerC Apr 19 '13 at 00:16
  • I don't understand your problem. Using an URL instead of the string is *trivial* and also the code has no *values* hardcoded, only the name of the elements is hardcoded (*rain*). I would assume that this name does not change. If it does, you need to extend your specification what you are trying to do and make all this hidden specifcs visible first. You probably have an understanding problem what the problem is you're dealing with? (That would be a non-technical problem). – hakre Apr 19 '13 at 07:20
-1

what about

<xsl:value-of select="number(RAIN_STRING_HERE)"/>
Alberto Fecchi
  • 1,705
  • 12
  • 27