Serializing a field to multiple nodes

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Serializing a field to multiple nodes

Andrew
I have a class

class Foo {
    int id;
    String name;
}

Using the JsonHierarchicalStreamDriver I can turn this into JSON:

{"id":1, "name":"x"}

I want to auto-create a secondary node which is link to the Resource in
a REST interface, ie.:

{"id":1, "link":"http://host/path/foo/1", "name":"x"}

I have managed to achieve this using a simple converter:

public class IdAndLinkConverter implements Converter {

     private final UriBuilder baseUriBuilder;
     private final String path;

     public IdAndLinkConverter(UriBuilder baseUriBuilder, String path) {
         this.baseUriBuilder = baseUriBuilder;
         this.path = path;
     }

     public boolean canConvert(Class type) {
         return Number.class.isAssignableFrom(type);
     }

     public void marshal(Object source, HierarchicalStreamWriter writer,
MarshallingContext context) {
         Number id = (Number) source;
         writer.setValue(id.toString());

         writer.endNode();
         writer.startNode("link");
         URI uri = baseUriBuilder.path(path).build(id);
         writer.setValue(uri.toString());
     }

     public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context) {
         throw new NotImplementedException();
     }
}

And I configure this to be associated to a specific field like this:

xstream.registerLocalConverter(Foo.class, "id", new
IdAndLinkConverter(baseUriBuilder, "/foo"));

(The javax.ws.rs.core.UriBuilder argument handles creation of the link.)

The "cheat" is the writer.endNode(), writer.startNode(..) which allows
me to inject a second node whilst I serialize the first field.

Job done.  Until I now want to build a more complex REST url, e.g.:

class Baa {
    int id;
    int fooId;
    String name;
}

{"id":7, "fooId":1, "link":"http://host/path/foo/1/bar/7", "name":"y"}

Now my link field requires two source fields to make it.  The converter
approach above does not allow access to the parent object so I am stuck.

OK, so I could write my own Converter for the Baa class; that's easy
enough, but not as elegant and long-term robust as the
IdAndLinkConverter.  Writing my own Converter requires this custom
Converter to be maintained as the Bar class is changed - i.e. adding new
fields.

I was thinking that I might be able to subclass the ReflectionConverter
which I believe handles all general classes.  I can see
marshallField(..) but this does not provide access to the original
object which I need to be able to get to both by id fields.

Another idea I had was to subclass ReflectionConverter and watch every
field go by and save the ones I am interesting in the
MashallingContext's put/get data holder.  Then when the second field I
want comes along I am good to go.  But I am not clear on the life cycle
of the MashallingContext and whether this is an appropriate usage of the
data holder.

No doubt someone has thought of this before.  What's the best way to
approach my requirement, please?

By the way, I only need to handle serialization or marhsalling, not
unmarhsalling.

Andrew.


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply | Threaded
Open this post in threaded view
|

Re: Serializing a field to multiple nodes

Jörg Schaible-2
Andrew wrote:

> I have a class
>
> class Foo {
>     int id;
>     String name;
> }
>
> Using the JsonHierarchicalStreamDriver I can turn this into JSON:
>
> {"id":1, "name":"x"}
>
> I want to auto-create a secondary node which is link to the Resource in
> a REST interface, ie.:
>
> {"id":1, "link":"http://host/path/foo/1", "name":"x"}
>
> I have managed to achieve this using a simple converter:
>
> public class IdAndLinkConverter implements Converter {
>
>      private final UriBuilder baseUriBuilder;
>      private final String path;
>
>      public IdAndLinkConverter(UriBuilder baseUriBuilder, String path) {
>          this.baseUriBuilder = baseUriBuilder;
>          this.path = path;
>      }
>
>      public boolean canConvert(Class type) {
>          return Number.class.isAssignableFrom(type);
>      }
>
>      public void marshal(Object source, HierarchicalStreamWriter writer,
> MarshallingContext context) {
>          Number id = (Number) source;
>          writer.setValue(id.toString());
>
>          writer.endNode();
>          writer.startNode("link");
>          URI uri = baseUriBuilder.path(path).build(id);
>          writer.setValue(uri.toString());
>      }
>
>      public Object unmarshal(HierarchicalStreamReader reader,
> UnmarshallingContext context) {
>          throw new NotImplementedException();
>      }
> }
>
> And I configure this to be associated to a specific field like this:
>
> xstream.registerLocalConverter(Foo.class, "id", new
> IdAndLinkConverter(baseUriBuilder, "/foo"));
>
> (The javax.ws.rs.core.UriBuilder argument handles creation of the link.)
>
> The "cheat" is the writer.endNode(), writer.startNode(..) which allows
> me to inject a second node whilst I serialize the first field.
>
> Job done.  Until I now want to build a more complex REST url, e.g.:
>
> class Baa {
>     int id;
>     int fooId;
>     String name;
> }
>
> {"id":7, "fooId":1, "link":"http://host/path/foo/1/bar/7", "name":"y"}
>
> Now my link field requires two source fields to make it.  The converter
> approach above does not allow access to the parent object so I am stuck.
>
> OK, so I could write my own Converter for the Baa class; that's easy
> enough, but not as elegant and long-term robust as the
> IdAndLinkConverter.  Writing my own Converter requires this custom
> Converter to be maintained as the Bar class is changed - i.e. adding new
> fields.
>
> I was thinking that I might be able to subclass the ReflectionConverter
> which I believe handles all general classes.  I can see
> marshallField(..) but this does not provide access to the original
> object which I need to be able to get to both by id fields.
>
> Another idea I had was to subclass ReflectionConverter and watch every
> field go by and save the ones I am interesting in the
> MashallingContext's put/get data holder.  Then when the second field I
> want comes along I am good to go.  But I am not clear on the life cycle
> of the MashallingContext and whether this is an appropriate usage of the
> data holder.
>
> No doubt someone has thought of this before.  What's the best way to
> approach my requirement, please?
>
> By the way, I only need to handle serialization or marhsalling, not
> unmarhsalling.

Actually you should have a look at a custom MarshallingStrategy. For JSON
you select normally a predefined one with xstream.setMode(NO_REFERENCES),
but in your case it makes sense to write an own one. The marshalling
strategy recognizes any marshalled object and you may keep there references
and ids for those objects and you may even write something additional
(normally attributes). For elements you may append own ones easily after the
converter is called that handles the object (note, that you cannot rely on
the definition sequence of JSON fields anyway).

Look at the TreeMarshaller and derived implementations to get an idea what
you can do - no need to write a converter though ;-)

Cheers,
Jörg


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email