I'm using jQuery to post ajax requests, and PHP to construct XML responses. Everything works fine, but I wonder about the method I've used for data typing, and whether there's a more standard way, or a more correct way. My XML generally looks like this, with some attributes representing text and other attributes representing numeric data:
<UnitConversions>
<UnitConversion basicUnitName="mile" conversionFactor="5280" conversionUnit="foot"/>
<UnitConversion basicUnitName="mile" conversionFactor="1760" conversionUnit="yard"/>
</UnitConversions>
I have a lot of different objects, not just this one type, so in my constructors, rather than initializing every property explicitly, I just copy the attributes over from the XML node:
var UnitConverter = function(inUnitConversionNode) {
var that = this;
$.each(inUnitConversionNode.attributes, function(i, attribute) {
that[attribute.name] = attribute.value;
});
};
I had trouble early on when I checked for numeric values, as in if(someValueFromTheXML === 1) -- this would always evaluate to false because the value from the XML was a string, "1". So I added nodes in key places in the XML to tell my client-side code what to interpret as numeric and what to leave as text:
<UnitConversions>
<NumericFields>
<NumericField fieldName="conversionFactor"/>
</NumericFields>
<UnitConversion basicUnitName="mile" conversionFactor="5280" conversionUnit="foot"/>
<UnitConversion basicUnitName="mile" conversionFactor="1760" conversionUnit="yard"/>
</UnitConversions>
So now I pass the NumericFields node into the constructor so it will know which fields to store as actual numbers.
This all works great, but it seems like a bit of a naive solution, maybe even a hack. Seems like there would be something more sophisticated out there. It seems like this issue is related to XML schemas, but my googling seems to suggest that schemas are more about validation, rather than typing, and they seem to be geared toward server-side processing anyway.
What's the standard/correct way for js to know which fields in the XML are numeric?
You can use isNaN() to detect whether the string is a number. For example isNaN("5043") returns false indicating that "5043" can be parsed as a number. Then, just use parseInt() to compare the value. For example:
if (parseInt(someValueFromTheXML, 10) === 1) {
...
}
Another way is to use loose comparison with the == operator so that "1" == 1 evaluates to true. However, it would be better practice to use the first suggestion instead. There is really no other way to go about this since XML/HTML attributes are always strings.
Related
I have an application where the user writes XPath queries to use as source data from a given document. Sometimes they need just the contents of an element, sometimes they need the whole element itself. To my understanding they should be able to specify either text() or node() at the end of their query to choose which behavior.
But it seems like the way I get a string out of the SimpleXMLElement determines the behavior, regardless of the query.
When I cast the query to (string), it ALWAYS only returns inner XML.
(string) $xml->xpath('//document/head/Keywords')[0] ===
(string) $xml->xpath('//document/head/Keywords/node()')[0] ===
(string) $xml->xpath('//document/head/Keywords/text()')[0] ===
'17';
If I use ->saveXML(), it ALWAYS returns the entire tag.
$xml->xpath('//document/head/Keywords')[0]->asXML() ===
$xml->xpath('//document/head/Keywords/node()')[0]->asXML() ===
$xml->xpath('//document/head/Keywords/text()')[0]->asXML() ===
'<Keywords topic="611x27keqj">17</Keywords>';
Is there a single way that I can get a string, which allows my users to specify inner vs outer XML as a part of their XPath query?
The SimpleXML xpath() method always returns SimpleXMLElement objects representing either an element or an attribute, never text. The methods you show in the question are the correct way to use that object to get text content or full XML.
If you want richer (but less simple) XPath functionality, you will have to use the DOM, and specifically the DOMXPath class. Note that you can freely mix SimpleXML and DOM using simplexml_import_dom and dom_import_simplexml; the internal representation is the same, so you can switch between the two "wrappers" with minimal cost.
So... I need to save a large-ish amount of data from a platform with an excruciatingly limited amount of memory.
Because of this, I'm basically storing the data on my webserver, using a php script to just write JSON to a flat file, because I'm lazy af.
I could go to the trouble of having it store the data in my mysql server, but frankly the flat file thing should have been trivial, but I've run up against a problem. There are several quick and dirty workarounds that would fix it, but I've been trying to fix it the "right" way (I know, I know, the right way would be to just store the data in mysql, but I actually need to be able to take the json file this produces and send it back to the platform that needs the data (In a ridiculously roundabout fashion), so it made sense to just have the php save it as a flat file in the first place. And It's already working, aside from this one issue, so I hate to reimpliment.
See... Because of the low memory on the platform I'm sending the json to my server from... I'm sending things one field at a time. Each call to the php script is only setting ONE field.
So basically what I'm doing is loading the file from disk if it exists, and running it through json_decode to get my storage object, and then the php file gets a key argument and a value argument, and if the key is something like "object1,object2", it explodes that, gets the length of the resulting array, and then stores the value in $data->$key[0]->$key[1].
Then it's saved back to disk with fwrite($file, json_encode($data));
This is all working perfectly. Except when $value is a simple string. If it's an array, it works perfectly. If it's a number, it works fine. If it's a string, I get null from json_decode. I have tried every way I can think of to force quotes on to the ends of the $value variable in the hopes of getting json_decode to recognize it. Nothing works.
I've tried setting $data->$key[0]->$key[1] = $value in cases where value is a string, and not an array or number. No dice, php just complains that I'm trying to set an object that doesn't exist. It's fine if I'm using the output of json_decode to set the field, but it simply will not accept a string on its own.
So I have no idea.
Does anyone know how I can either get json_decode to not choke on a string that's just a string, or add a new field to an existing php object without using the output of json_decode?
I'm sure there's something obvious I'm missing. It should be clear I'm no php guru. I've never really used arrays and objects in php, so their vagaries are not something I'm familiar with.
Solutions I'm already aware of, but would prefer to avoid, are: I could have the platform that's sending the post requests wrap single, non-numeric values with square braces, creating a single item array, but this shouldn't be necessary, as far as I'm aware, so doing this bothers me (And ends up costing me something like half a kilobyte of storage that shouldn't need to be used).
I could also change some of my json from objects to arrays in order to get php to let me add items more readily, but it seems like there should be a solution that doesn't require that, so I'd really prefer not to...
I skim through your post.
And I know this works for StdClass :
$yourClass->newField = $string;
Is this what you wanted ?
OK so... ultimately, as succinctly as possible, the problem was this:
Assuming we have this JSON in $data:
{
"key1":
{
"key2":["somedata","someotherdata"]
}
}
And we want it to be:
{
"key1":
{
"key2":["somedata","someotherdata"],
"key3":"key3data"
}
}
The php script has received "key=key1,key3&value=key3data" as its post data, and is initialized thusly:
$key = $_POST["key"];
$key = explode($key,",");
$value = $_POST["value"];
...which provides us with an array ($key) representing the nested json key we want to set as a field, and a variable ($value) holding the value we want to set it to.
Approach #1:
$data->$key[0]->$key[1] = json_decode($value);
...fails. It creates this JSON when we re-encode $data:
{
"key1":
{
"key2":["somedata","someotherdata"],
"key3":null
}
}
Approach #2:
$data->$key[0]->$key[1] = $value;
...also fails. It fails to insert the field into $data at all.
But then I realized... the problem with #2 is that it won't let me set the nonexistent field, and the problem with approach #1 is that it sets the field wrong.
So all I have to do is brute force it thusly:
$data->$key[0]->$key[1] = json_decode($value);
if (json_decode($value) == NULL)
{
$data->$key[0]->$key[1] = $value;
}
This works! Since Approach #1 has created the field (Albeit with the incorrect value), PHP now allows me to set the value of that field without complaint.
It's a very brute force sort of means of fixing the problem, and I'm sure there are better ones, if I understood PHP objects better. But this works, so at least I have my code working.
Trying to decide which is more appropriate for my use case...
After comparing the documentation for these methods, my vague understanding is evaluate returns a typed result but query doesn't. Furthermore, the query example includes looping through many results but the evaluate example assumes a single typed result.
Still not much the wiser! Could anyone explain (in as close as possible to layman's terms) when you would use one or the other - e.g. will the multiple/single results mentioned above always be the case?
DOMXPath::query() supports only expressions that return a node list. DOMXPath::evaluate() supports all valid expressions. The official method is named evaluate(), too: http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator
Select all p elements inside a div: //div//p
Select all href attributes in a elements the current document: //a/#href
You can use the string() function to cast the first element of a node list to a string. This will not work with DOMXpath::query().
Select the title text of a document: string(/html/head/title)
There are other function and operators that will change the result type of an expression. But it is always unambiguous. You will always know what type the result is.
query will return a DOMNodeList regardless of your actual XPath expression. This suggests that you don't know what the result may be. So you can iterate over the list and check the node type of the nodes and do something based on the type.
But query is not limited to this use case. You can still use this when you know what type you will get. It may be more readable in the future what you wanted to achieve and therefore easier to maintain.
evaluate on the other hand gives you exactly the type that you select. As the examples point out:
$xpath->evaluate("1 = 0"); // FALSE
$xpath->evaluate("string(1 = 0)"); // "false"
As it turns out selecting attributes //div/#id or text nodes //div/text() still yields DOMNodeList instead of strings. So the potential use cases are limited. You would have to enclose them in string: string(//div/#id) or text nodes string(//div/text()).
The main advantage of evaluate is that you can get strings out of your DOMDocument with fewer lines of code. Otherwise it will produce the same output as query.
ThW's answer is right that some expressions will not work with query:
$xpath->query("string(//div/#id)") // DOMNodeList of length 0
$xpath->evaluate("string(//div/#id)") // string with the found id
Ok, am trying to find a character or group of characters, or something that can be used that I can explode from, since the text is user-defined, I need to be able to explode from a value that I have that can never be within the text.
How can I do this?
An example of what I'm trying to do...
$value = 'text|0||#fd9||right';
Ok,
text is something that should never change in here.
0, again not changeable
#fd9 is a user-defined string that can be anything that the user inputs...
and right sets the orientation (either left or right).
So, the problem I'm facing is this: How to explode("||", $value) so that if there is a || within the user-defined part... Example:
$value = 'text|0||Just some || text in here||right';
So, if the user places the || in the user-defined part of the string, than this messes this up. How to do this no matter what the user inputs into the string? So that it should return the following array:
array('text|0', 'Just some || text in here', 'right');
Should I be using different character(s) to explode from? If so, what can I use that the user will not be able to input into the string, or how can I check for this, and fix it? I probably shouldn't be using || in this case, but what can I use to fix this?
Also, the value will be coming from a string at first, and than from the database afterwards (once saved).
Any Ideas?
The problem of how to represent arbitrary data types as strings always runs up against exactly the problem you're describing and it has been solved in many ways already. This process is called serialization and there are many serialization formats, anything from PHP's native serialize to JSON to XML. All these formats specify how to present complex data structures as strings, including escaping rules for how to use characters that have a special meaning in the serialization format in the serialized values themselves.
From the comments:
Ok, well, basically, it's straight forward. I already outlined 13 of the other parameters and how they work in Dream Portal located here: http://dream-portal.net/topic_122.0.html so, you can see how they fit in. I'm working on a fieldset parameter that basically uses all of these parameters and than some to include multiple parameters into 1. Anyways, hope that link helps you, for an idea of what an XML file looks like for a module: http://dream-portal.net/topic_98.0.html look at the info.xml section, pay attention to the <param> tag in there, at the bottom, 2 of them.
It seems to me that a more sensible use of XML would make this a lot easier. I haven't read the whole thing in detail, but an XML element like
<param name="test_param" type="select">0:opt1;opt2;opt3</param>
would make much more sense written as
<select name="test_param">
<option default>opt1</option>
<option>opt2</option>
<option>opt3</option>
</select>
Each unique configuration option can have its own unique element namespace with custom sub-elements depending on the type of parameter you need to represent. Then there's no need to invent a custom mini-format for each possible parameter. It also allows you to create a formal XML schema (whether this will do you any good or not is a different topic, but at least you're using XML as it was meant to be used).
You can encode any user input to base64 and then use it with explode or however you wish.
print base64_encode("abcdefghijklmnopqrstuvwxyz1234567890`~!##$%^&*()_+-=[];,./?>:}{<");
serialized arrays are also not a bad idea at all. it's probably better than using a comma separated string and explode. Drupal makes good use of serialized arrays.
take a look at the PHP manual on how to use it:
serialize()
unserialize()
EDIT: New Solution
Is it a guarantee that text doesn't contain || itself?
If it doesn't, you can use substr() in combination with strpos() and strrpos() instead of explode
Here's what I usually do to get around this problem.
1) capture user's text and save it in a var $user_text;
2) run an str_replace() on $user_text to replace the characters you want to split by:
//replace with some random string the user would hopefully never enter
$modified = str_replace('||','{%^#',$user_text);
3) now you can safely explode your text using ||
4) now run an str_replace on each part of the explode, to set it back to the original user entered text
foreach($parts as &$part) {
$part = str_replace('{%^#','||',$part);
}
I'm developing a floorplanner Flex mini application. I was just wondering whether JSON or XML would be a better choice in terms of performance when generating responses from PHP. I'm currently leaning for JSON since the responses could also be reused for Javascript. I've read elsewhere that JSON takes longer to parse than XML, is that true? What about flexibility for handling data with XML vs JSON in Flex?
I'd go with JSON. We've added native JSON support to Flash Player, so it will be as fast on the parsing side as XML and it's much less verbose/smaller.
=Ryan ryan#adobe.com
JSON is not a native structure to Flex (strange, huh? You'd think that the {} objects could be easily serialized, but not really), XML is. This means that XML is done behind the scenes by the virtual machine while the JSON Strings are parsed and turned into objects through String manipulation (even if you're using AS3CoreLib)... gross... Personally, I've also seen inconsistencies in JSONEncoder (at one point Arrays were just numerically indexed objects).
Once the data has been translated into an AS3 object, it is still faster to search and parse data in XML than it is with Objects. XPath expressions make data traversal a pleasure (almost easy enough to make you smile compared to other things out there).
On the other hand JS is much better at parsing JSON. MUCH, MUCH BETTER. But, since the move to JavaScript is a "maybe... someday..." then you may want to consider, "will future use of JSON be worth the performance hit right now?"
But here is a question, why not simply have two outputs? Since both JS and AS can provide you POSTs with a virtually arbitrary number of variables, you really only need to concern yourself with how the server send the data not receives it. Here's a potential way to handle this:
// as you are about to output:
$type = './outputs/' . $_GET[ 'type' ] . '.php';
if( file_exists( $type ) && strpos( $type, '.', 1 ) === FALSE )
{
include( $type );
echo output_data( $data );
}
else
{
// add a 404 if you like
die();
}
Then, when getting a $_GET['type'] == 'js', js.php would be:
function output_data( $data ){ return json_encode( $data ); }
When getting $_GET['type'] == 'xml', xml.php would hold something which had output_data return a string which represented XML (plenty of examples here)
Of course, if you're using a framework, then you could just do something like this with a view instead (my suggestion boils down to "you should have two different views and use MVC").
No, JSON is ALWAYS smaller than XML when their structures are completely same. And the cost of parsing text is almost up to the size of the target text.
So, JSON is faster than XML and if you have a plan to reuse them on javascript side, choose JSON.
Benchmark JSON vs XML:
http://www.navioo.com/ajax/ajax_json_xml_Benchmarking.php
If you're ever going to use Javascript with it, definitely go with JSON. Both have a very nice structure.
It depends on how well Flex can parse JSON though, so I would look into that. How much data are you going to be passing back? Error/Success messages? User Profiles? What kind of data is this going to contain?
Is it going to need attributes on the tags? Or just a "structure". If it needs attributes and things like that, and you don't want to go too deep into an "array like" structure, go with XML.
If you're just going to have key => value, even multi-dimensional... go with JSON.
All depends on what kind of data you're going to be passing back and forth. That will make your decision for you :)
Download time:
JSON is faster.
Javascript Parse
JSON is faster
Actionscript Parse
XML is faster.
Advanced use within Actionscript
XML is better with all the E4X functionality.
JSON is limited with no knowledge of Vectors meaning you are limited to Arrays or will need to override the JSON Encoder in ascorelib with something such as
else if ( value is Vector.<*> ) {
// converts the vector to an array and
return arrayToString( vectorToArray( value ) );
} else if ( value is Object && value != null ) {
private function vectorToArray(__vector:Object):Array {
var __return : Array;
var __vList : Vector.<*>;
__return = new Array;
if ( !__vector || !(__vector is Vector.<*>) )
{
return __return;
}
for each ( var __obj:* in (__vector as Vector.<*>) )
{
__return.push(__obj);
}
return __return;
}
But I am afraid getting those values back into Vectors is not as nice. I had to make a whole utility class devoted to it.
So which one is all depending on how advanced your objects you are going to be moving.. More advanced, go with XML to make it easier ActionScript side.
Simple stuff go JSON