PHP access nested object properties from a string literal - php

I'm trying to come up with a simple approach for extracting data from a multitude of json data sources where the reference is stored in a string literal. This is easy to do when the object structure is known but is there any way to get php to interpret the string literal as it would directly in code?
$data = json_decode('
{
"foo": 1,
"object": {
"foo": 1.1,
"bar": 1.2
},
"array": [
"foo",
"bar"
]
}
');
// Explicitly coded reference all work, of course
var_dump($data->foo); // int(1)
var_dump($data->object->foo); // float(1.1)
var_dump($data->array[0]); // string(3) "foo"
And I know that I can access an object's property from a string literal like this:
$string = 'foo';
var_dump($data->$string); // int(1)
var_dump($data->object->$string); // float(1.1)
Is there any way to go about achieving this for the second and third references, without having to break up the string and loop through the object recursively until each element is found?
$string = 'object->foo';
var_dump($data->$string);
$string = 'array[0]';
var_dump($data->$string);
If there is nothing native to PHP for handling this, is there a library or anything that I could use to make this process easier?
Edit: To provide some extra clarity, this is an application where a JSON feed can be configured through the UI and an administrator can extract values from the JSON feed. This works fine for simpler JSON structures but breaks down when they're trying to pull a value such as $foo->bar->baz[0]->qux->corge. I'm trying to find an easier way to get values such as this when the structure changes and I can't account for every possibility within the code.

Since you are using JSON, i‘d use JSON Pointers. Here is their official spec
What they do is providing a query language for json objects (similar to XPath for XML). Accessing your foo within object would result in a query like /object/foo.
Now you‘d have your users specify the json pointer queries, and use a json pointer library to perform the actual queries. This has the advantage that if you ever decide to ditch PHP for say Java, you’d only need to use a JSON Pointer implementation for Java and your users won‘t be impacted by the change.

Related

Understanding the "_id" key in JSON results accessed from a PHP file.

PHP
$query = $myCollection->findOne(array("field2.sf2" => "value two"));
echo json_encode($query);
Returned JSON Object
{"_id":{"$id":"5476854783473474578548"},"field1":"value one","field2":{"sf1":["av1","av2","av3"],"sf2":"value two"},"field3":"value three"}
What is happening at:
"_id":{"$id":"5476854783473474578548"}
I can see it is the representation of the Document's "_id" key and value ie:
"_id": ObjectId("5476854783473474578548")
But a few things are happening:
The value becomes a sub document ie it is surrounded in curly braces
ObjectId is being replaced by "$id"
I'm using MongoDB, accessed by a PHP file, via jQuery's getJSON() method.
Are there any gotcha's i need to look out for with this happening?
Any commonly known 'industry knowledge' tips that could be helpful to a MongoDB newbie or further explain what is happening?
The objectId Is not being replaced but merely serialised to string form when you call json_encode.
Its properties are being taken out, placed inside stringified JSON and sent over the wire where then in JQuery the library parses that stringified JSON into the object you see.
No data has been lost.
The only gotcha I can think of is that the constructor for MongoId does not actually take this object back in, it assumes only a hexadecimal ObjectId (i.e. 5476854783473474578548) http://www.php.net/manual/en/mongoid.construct.php

An alternative to returning a delimited string?

I have a whole suite of PHP scripts that interact with both the Android and iOS version of a mobile app. They all work the same: After the mobile app initiates a GET or POST, the PHP script typically returns a dash delimited string.
e.g.
If I want to get a list of the comments on a particular page, the PHP script would return something like
user1-comment1-user2-comment2
Is there a better way than this? Because if I ever want to return a new variable e.g.
user1-comment1-newValue1-user2-comment2-newValue2
then this will break all current versions of the mobile app.
Why not serialize the result and parse it in java? You could also use json_encode in PHP and decode it in your android app... see How to parse JSON in Android
You need to use a serializer. If you have a user name that contains a - you'll run into problems. Serializers take care of this for you. The current favorite is JSON, used to be XML.
JSON has excellent support in most web languages.
You can serialize your array of data into either a json string or a message pack string. Let's say your php array was:
$a = array(
"user1" => "comment1",
"user2" => "comment2",
"user3" => "comment3"
);
That would translate to this in json:
{
"user1": "comment1",
"user2": "comment2",
"user3": "comment3"
}
This json string can be easily converted to NSDictionaries on ios (using NSJSONSerialization) and JSONObjects on android (tutorial here).
A message packed string would be similar in structure to the json string above, but is less human readable because of its more compact nature. However, both Java and Objective-C have libraries to help translate messaged packed data into native objects.
Using JSON, you can make use of name/value pairs, so the order or inclusion of the parameters won't matter. JSON also provides hierarchy and limited typing, such as number versus string.
JSON also allows you to easily escape characters, so you could have any value you want (even with backslashes and quotes.

Accessing PHP objects using both naming with reserved characters (-,#) and indexes (0)

In PHP figured out how to use {name-with-reserved-chars} notation to access object data after JSON decode but I also have index values as well in the path (like [0]). My attempts to add the index value has returned nothing despite all my attempts to vary the pattern. Here is an example based on a big JSON object:
["ops:world-patent-data"]["exchange-documents"]["exchange-document"]
[0]["bibliographic-data"]["publication-reference"]["document-id"][0].date.$
my attempt gets nothing:
print $result->{'ops:world-patent-data'}->{'exchange-documents'}->{'exchange-document'}->
{0}->{'bibliographic-data'}->{'publication-reference'}->{'document-id'}->{0}->date;
wondering how to place the 0 and 1 indexes in the path ...
When deserializing an actual JSON list (i.e, not an object but an actual array with numerical indices; something like [1,2,3,...] or also [{...},{...},...]), PHP's parse_json function builds a corresponding PHP array with numerical indices (while on the other hands, it maps JSON objects to instances of the stdClass class -- unless you use the $assoc parameter of the parse_json function).
So, because you are accessing arrays instead of objects, your code would probably have to look like this:
print $result->{'ops:world-patent-data'}->{'exchange-documents'}->
{'exchange-document'}[0]->{'bibliographic-data'}->{'publication-reference'}->
{'document-id'}[0]->date;
It would probably easier to use the $assoc parameter, forcing json_decode to map the entire JSON object with associative arrays, allowing you to use the square bracket notation for object access also.

Serialize/unserialize PHP object-graph to JSON

I wanted to serialize a complete PHP object-graph to a JSON string representation, and unserialize it back to an identical PHP object-graph.
Here is a summary of options I considered, and reasons why they don't work for me:
serialize() doesn't do what I want, because it uses a format specific to PHP. I want a format that is widely supported by most languages, and human-readable/editable.
json_encode() doesn't do what I want, because it only does simple values and arrays, not objects. (I'm actually using this in my implementation, see below.)
var_export() doesn't handle circular references, and doesn't do what I want (see above.) (note that my current implementation does not handle circular references either - see comments and reply below for clarification of this issue.)
Sebastian Bergmann's Object Freezer is a nice implementation, but it doesn't do what I want either - it uses a very long form, and relies on stuffing serialized objects with GUIDs.
Serialized doesn't do what I want - it does not actually perform serialization, it parses the output of serialize() and produces a different representation, e.g. XML, but is unable to parse that representation. (it also does not support JSON - XML is very long form, and is not what I want.)
I now have a working implementation to share:
https://github.com/mindplay-dk/jsonfreeze
The JSON-representation of the object-graph looks like this:
{
"#type": "Order",
"orderNo": 123,
"lines": [{
"#type": "OrderLine",
"item": "milk \"fuzz\"",
"amount": 3,
"options": null
}, {
"#type": "OrderLine",
"item": "cookies",
"amount": 7,
"options": {
"#type": "#hash",
"flavor": "chocolate",
"weight": "1\/2 lb"
}
}],
"paid": true
}
This approach is designed to work for a pure tree-structure aggregate - circular references are not allowed, nor multiple references to the same objects. In other words, this is not general-purpose like e.g. serialize() and unserialize() which function for any PHP object-graph.
In my initial approach I used a serialized form that was essentially a base-0 list of objects. The first object in the list (number 0) is the root of the serialized object-graph, any other objects are stored in the order they're found.
In the current implementation, the JSON representation resembles the original tree-structure to the extend that this is possible, making it possible to actually work with the JSON representation of an object-graph in JavaScript. The only deviation is the magic #type property (prefixed with # to prevent collision with property-names) and the #hash "type", used to distinguish array-type hashes (stored as JSON objects) from regular array-type arrays (stored as JSON arrays).
I'm leaving these notes about the previous version here for historical purposes.
Circular references are handled simply by never storing nested objects inside the serialized representation of each object - instead, any object-reference is stored as a JSON-object with the object-index - e.g. {"__oref":2} is a reference to the object with index 2 in the object-list.
I'm having a problem with array-references in my implementation - when I var_dump() inside the code that restores references to objects to the array, they are being populated, but at some point the array gets copied, and you end up with the empty copy. I've tried placing & characters everywhere in the code, but regardless of where I pass by reference, the end-result is an empty array.
The finished script (posted above) meets my precise requirements:
Serialize and unserialize an entire aggregate.
Have a JSON representation that closely resembles the original data-structure.
Do not pollute the data-structure with dynamically generated keys or other data.
It does not handle circular references. As pointed out in a comment above there is no right way to store circular references or multiple references to the same object, as these are all equal. Realizing this, I decided my object-graph must be a regular tree, and accepted this limitation as "a good thing".
update: the ouput can now be formatted with indentation, newlines and whitespace - it was important for me to have a human-readable (and source-control friendly) representation for my purposes. (The formatting can be enabled or disabled as needed.)
I don't know if this is what you are after, but if you are only interested in getting at public properties of a object, get_object_vars($obj) will do the trick.
<?php
class foo {
public $fname = "John";
public $sname = "Doe";
private $status = null;
static $type = "person";
}
$obj = new foo;
print_r( (get_object_vars($obj)) );
print json_encode(get_object_vars($obj));
?>
Will output :
Array ( [fname] => John [sname] => Doe )
{"fname":"John","sname":"Doe"}
The above method is useless for accessing function references and private variables but you might be able to use this in conjunction with some more code to knock up what you want.
Dinesh.

Should I create wrappers for all my RESTful JSON exchanges?

I have multiple calls to many RESTful services. I translate to PHP using native PHP json_decode when I receive the data, and use json_encode when sending data.
My concern is that with deeply nested data I end up writing code like:
$interestType = $person['children'][$i]['interests'][$j]['type'];
This can get quite messy. I feel there would be some benefit in creating objects whose methods/instance variables wrap around these structures, such that I could do:
$interestType = $person->getChild($i)->getInterest($j)->getType();
It seems clearer to me, but in reality it's not much more concise.
What are the benefits of just doing everything using native PHP arrays, and writing wrapper classes for each REST resource?
My concern is that I will have to write custom encode/decode functions to map to these wrappers.
I am not familiar with the implementation of objects in PHP, but reading this blogpost about array vs object performance, it seems the overhead is minimal. So I guess it boils down to style preferences. A simple (not-nested) array to object converter can be found here:
http://www.lost-in-code.com/programming/php-code/php-array-to-object/
A compromise, which would be trivial to implement:
<?php
$json = '{"a": [{"aa" : 11}, {"ab" : 12}],"b":2,"c":3,"d":4,"e":5}';
$o = json_decode($json); // plain object
$a = json_decode($json, true); // this will yield an array
echo $o->a[0]->aa;
?>
json_decode takes an optional argument, that determines, if the supplied JSON is converted to an associative array. If it isn't ($o), you have half the syntax, you aim for.

Categories