views:

458

answers:

2

For context, we are storing most of our data as JSON strings. This works very well with Hadoop on the backend and is easy to handle in Ruby on the front end. My data types fit the natural pattern for inheritance.

For simplicity, lets say I have a class Pet and a process FeedPet that feeds a pet. I also have a process WalkDog that only applies to Dog, which is a kind of pet. My data is organized such that I never need to worry about trying to walk a pet that isn't a dog.

What I would like to do is have Pet and Dog extends Pet, with Dog having an additional method "getLeash()", but I can't figure out how to map this to JSON.

Instead, I have a class Pet with a species data hashmap, so the WalkDog process would call pet.getSpeciesData("leash") instead of dog.getLeash().

I can create a Dog extends Pet, and I can serialize that to JSON using the Jackson library. The JSON object will have a leash field. But assume that I want to feed all the pets. All pets have a getFood() method. So the FeedPet process deserializes the objects to Pet. But this loses the leash field.

The WalkDog process can do this because it knows all of its input is going to be Dogs, so it can read it as a Dog and write it back out as a Dog.

Is there any way to serialize java objects to JSON such that I can preserve their type? I'm thinking something like Rails single table inheritance, but it would have to be something that the JSON libraries understand.

A: 

I've not had that much experience using JSON other than for fixtures in django, but in that instance I just have a "model" key, with the associated value.

It is then up to the program that is interpreting the objects to determine their type and inheritance hierarchy.

What I'm trying to say is that it is irrelevant what methods are available, as this doesn't need to be serialised. Only the object's name and attributes.

-- update

After reading your question again, it seems like your process is deserialising into the parent class, rather than the actual class. Since your dog class inherits from pet, you just want to make sure your deserialiser is creating objects of the most specialised class.

I don't know about the Jackson library, but if you can set it to have a type of model field, then that might be the go.

Matthew Schinckel
+2  A: 

To make it work you must both embed some object type information in data (where it is only useful for deserializing) and usually use external schema definition which otherwise would not be needed (like XML Schema for xml; since that's basically a generic type system). This has same problems as ORM has: Hibernate has to use ugly work-arounds (n+1 - way joins, "super tables" or discriminator fields).

Or another way to put it: data mapping/binding is not quite the same as object serialization/deserialization (latter tries to preserve more of object identity).

Here I am assuming that what you want is basically something like:

Pet pet = mapper.readValue(jsonString, Pet.class); // (and possibly get an exception if Pet is an abstract class...) Leash l = ((Dog) pet).getLeash();

If this is not the case, you could just simply bind to Dog

Dog dog = mapper.readValue(jsonString, Dog.class);

So anyway: Jackson project has feature request for doing just this:

http://jira.codehaus.org/browse/JACKSON-91

which allow you to do what (I think) you want.

However, this solution will mostly work for Java, as there is no standard way of passing Object type info within JSON. With XML this can sort of be done with XML Schema defined "xsi:type" attribute; which identifies Schema type, which is then mapped to class (yes, rather complicated way, but it does work).

UPDATE: Jackson 1.5 added support for this (i.e. implemented JACKSON-91 feature request), so it can be used for generating type identifiers, to allow proper handling of polymorphic types. It should work with non-Java systems too, given that you can fully configure details of how type information is to be included; and is NOT limited to using Java class names.

StaxMan
I think you've answered the core of it, there is no standard way of representing this in JSON. JACKSON-91 would give me exactly what I want, since in my case, I am only reading the data in Ruby.
Kevin Peterson
Ah ok. Actually, 91 would only be needed if you had to read it in Java. To write class info there might be easier ways (although 91 would also cover this obviously). One way would be to make Object.getClass() serializable (SerializationConfig.Feature.OUTPUT_CLASS_NAME ?).
StaxMan