Thanks for the reply John, your response is really appreciated.
Sorry if this post is a bit long, but hopefully it's in some way
useful. As for the deep philosophical question about what jsonpickle
is: 1) a replacement for pickle; or 2) an extension to json, I'd kinda
have to go with 3) a bit of both, with more than a fair share of
emphasis on 2; jsonpickle could extend and then improve on json by
adding a bit more pickle.
To my mind, the most important thing json or jsonpickle should do is
maintain data across languages. In your json example, data is not
lost: ['hello', 'hello'] arrives in JSON almost intact, all we're
missing is the meta data which describes the relationship between the
two strings in the list (i.e. being the same object), but at least we
know what those strings should contain.
In my original example, jsonpickle loses data and meta data. The
object o4 leaves python with the data "Object 3, shared with o4" and
returns as "Object 1, shared with o2". Now, not only have we lost our
meta data, but we've also lost our data.
Sure, I could extend json's JSONEncoder
http://docs.python.org/library/json.html#json.JSONEncoder
and JSONDecoder classes to describe my objects and how they interact
with each other, but where's the fun in that? jsonpickle is doing so
much of the hard work for me already, (ah, laziness, the foundation of
all good programming) wouldn't it be great if jsonpickle could to the
rest of the hard work too?
You mention your concern about...
> the Javascript side and the JSON
> portability. While we might be able to maintain references in Python,
> we would lose all such references in Javascript.
I don't think we need to be concerned about JSON or Javascript. All we
have to be concerned about giving enough data, and meta data, to allow
whatever the other language (Javascript or otherwise) needs, to be
able to do whatever it has to do to put the objects back together
again. If we've done our job right in the encoding, we should trust
that the target language can do its thing in the decoding.
As you say, we need a use case to see why this is useful. Here's mine.
I'm working on a module to allow "users" to create a document. This
document happens to be a questionnaire. The questionnaire can be made
up of a collection of question objects. Questions can have unique data
(like name) and can also share some data (like answers). Here's an
example:
Q1) What operating system(s) do you use for your development?
Ubuntu
Mac OS X
Windows XP
Q2) When developing, which operating system do you enjoy using the
most?
Ubuntu
Mac OS X
Windows XP
Let's say you want to define 100 questions with the same answer list.
You'd make your answer list an object (or a collection of Text
objects) and make it an attribute of your Question object. Some time
in the future, you decide to extend your answer list with a new
answer, or rename one of the existing answers to correct a spelling,
or reflect some other change. You don't want to do it 100 times, you
want to do it once and have that change automatically updated
everywhere. Easy when you're dealing with shared objects.
Here's my original example extended a bit to show such a use case.
Copy it to a file and run it, you'll see what I mean.
import jsonpickle
class Shared(object):
# A bit like a question
def __init__(self, name, text):
self.name = name
self.text = text
class Text(object):
# A bit like an answer
def __init__(self, text):
self.text = text
def shout(self):
# Do something with a text
return '%s!!' % self.text
def update(self, text):
# Change a text
self.text = text
def report(l):
for o in l:
print('%s = %s' % (
o.name, o.text.shout(),))
if __name__=='__main__':
o1 = Shared( 'o1', Text('Object 1, shared with o2') )
o2 = Shared( 'o2', o1.text )
o3 = Shared( 'o3', Text('Object 3, shared with o4') )
o4 = Shared( 'o4', o3.text )
list1 = [o1, o2, o3, o4]
print('list1, before changes')
report(list1)
# Now, let's change some text.
o2.text.update('Object 2, shared with o1')
print('list1, after changes')
report(list1)
list2 = jsonpickle.decode( jsonpickle.encode(list1) )
print('list2, after jsonpickle')
report(list2)
Phew, I told you this was a long post. I won't post the code, but here
http://remote.ronin.com/jsonpickle/ is the Javascript solution for the
same problem. View the source on the link, you'll see the similarities
between the Python and the Javascript objects. Javascript works just
like Python. Javascript is capable to keeping references in the same
way Python does. We can trust Javascript to do it's job, if it has the
data to do it.
Now, coming to a conclusion at last (inhales deeply, then drinks tea).
Without working out the details, here's how I might go about
maintaining references in jsonpickle. Firstly, let's look at the JSON
produced by jsonpickle in my new example above. We'll only look at
half of it:
[
{
"py/object": "__main__.Shared",
"text": {
"py/object": "__main__.Text", # Here's jsonpickle's meta data
do say "This is a Text object"
"text": "Object 2, shared with o1"
},
"name": "o1"},
{
"py/object": "__main__.Shared",
"text": {
"py/ref": "/text" # Here's jsonpickle's meta data to day
"This is a reference to an object"
},
"name": "o2"
},
.......
]
Now, if we added more meta data (i.e. the dictionary keys beginning
'py/'), we could allow for the references to be rebuilt in our target
language. Here I'm adding the output of Python's built-in id() to two
new keys: "py/object_id" and "py/ref_id". When this data arrives in
Javascript, I can have my Javascript look up the "py/ref" object via
it's "py/ref_id" and remake the references. In Python, our jsonpickle
UnPickler class can do the same. Make sense?
[
{
"py/object": "__main__.Shared",
"text": {
"py/object": "__main__.Text",
"py/object_id": 123456, # Or whatever unique int id()
assigns. It doesn't matter, so long as it's unique.
"text": "Object 2, shared with o1"
},
"name": "o1"},
{
"py/object": "__main__.Shared",
"text": {
"py/ref": "/text",
"py/ref_id": 123456 # Our unique int as assigned by id()
above.
},
"name": "o2"
},
....
]
I think this goes some way towards maintaining object references in
jsonpickle. It's certainly something I could do with.
Thanks again for replying John, hopefully you were able to read to the
end :)
Kieran.