views:

351

answers:

3

I'm not sure how to best explain this, so this may be a bit long, please bear with me.

Let's say I have a java bean with a bunch of properties:

public interface Customer {
    public String getFirstName();
    public String getLastName();
    public Address getAddress(); // embedded bean with its own properties
    ... // lots more
}

There's a corresponding implementation, obviously, plus appropriate setters are defined as well (though setters may not be exposed in the interface for some properties).

There's a service (POJO interface + implementation) that defines a bunch of operations that can be performed on customers (CRUD, some find() queries, etc...). Service is bootstrapped via Spring and the whole thing works fine locally and via RMI.

Now I have a client (iPhone app) which needs access to a small subset of functionality exposed via above API - subset both in terms of service operations and API properties. The idea is to expose this subset via REST-style API:

GET /services/customers/1234

returns

<customer>
  <firstName>Homer</firstName>
  <lastName>Simpson</lastName>
  <city>Springfield</city>
</customer>

or maybe even

<customer firstName="Homer" lastName="Simpson" city="Springfield" />

The question is - how do I go about doing this? I've (briefly) looked at JAXB / CXF / Jersey and it looks like I'd have to define a schema, generate a bunch of classes based on it and copy data from my API entities to those classes. Is there a way to avoid all that?

Ideally I'd like to annotate the appropriate properties in my API entities and have them (and only them) marshalled "automagically". I'm ok with writing dedicated interface / implementation for the web service endpoint if needed; annotating the existing one would be even better.

The other (more pressing) question is - am I doing something stupid? Is there a simpler and / or better way? Last time I've worked with web services was Axis 1.1 times and last time I've worked with iPhone was never :-) so any pointers will be much appreciated.

Update: To clarify - I'm looking for the simplest way to marshall a subset of javabean properties to XML (or JSON maybe?) without having to write / generate an additional class to represent said subset of properties. The above example is an oversimplification, in reality the API is quite extensive with many classes involved. I really want to avoid duplication if possible.

+1  A: 

Googling jaxb interface seems to indicate interfaces are a pain to work with in JAXB, as seen in this java.net blog post and another stackoverflow question.

It may be easier if your POJO is a class rather than an interface. I'm using RESTEasy (another JAX-RS implementation) with JAXB and I only need to annotate my POJO class with @XmlRootElement. On the other side, I've written custom http client code to marshall/unmarshall the same annotated class with JAXB (would be automagic with RESTEasy client proxy framework if I can use that). Note that I don't have a schema defined, but I can get away with a lot since I'm in control of both sides of the RESTful web service.

Jersey has a client API you might want to take a look at. CXF also has a front-end / client API including a proxy-based mechanism similar to RESTEasy.

Take a look at the RESTful Java with JAX-RS book which covers the following that I've found helpful:

  • REST service concepts
  • overview of the various implementations including client API support
  • code examples

Example controller:

@GET
@Produces( { MediaType.APPLICATION_XML })
@Path("/customers/{id}")
public Customer getCustomer(@PathParam("id") int id) {
    ...
    return customer;
}

Example POJOs:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
  Address address;
  String name;

  // annotate with XmlTransient to prevent mapping this property/type to XML
  @XmlTransient
  String ssn;
  ...
}

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
  String planet;
  ...
}

XmlTransient Java doc

marklai
Thank you for your answer. I do have the actual bean classes; interfaces are there to keep a clean separate API, nothing more. I'm looking at RESTEasy, but it doesn't seem to be fundamentally different from CXF / Jersey; they all seem to use JAXB for marshalling and I don't see how to skip the interim step of creating (generating) the XML schema and mapped classes for it.
ChssPly76
They are different flavors of the JAX-RS implementation. I don't have an XML schema defined and had no problems marshalling/unmarshalling the POJOs even via custom client code that creates a JAXB marshaller/unmarshaller to do the work. I've updated the answer with code samples. Note the action method is returning the POJO which is the automagic that RESTEasy provides (probably same for the other implementations) to deal with the JAXB annotated POJOs w/o an XML schema.
marklai
In your code example `Address` should be annotated as `@XmlType` rather than `@XmlRootElement`, no? But my problem here is that I only want `name` and `planet` in the resulting xml, not the `dateOfBirth`, `street`, `phone` or many other properties that are defined in `Customer` / `Address`. I can't (so far) see a way to do that in JAXB. Anywho, thanks - I'll get the book you've recommended and will read JAXB docs in more detail too.
ChssPly76
Use @XmlTransient to leave out properties you don't want mapped to XML. Examples updated in answer. According to the javadocs, both @XmlType and @XmlRootElement can be used to map a top level class or an enum type. https://jaxb.dev.java.net/nonav/2.2-ea/docs/api/ I'm sure there are subtle differences I haven't had the need to deal with yet.
marklai
I've ultimately went with a somewhat different approach, but your answer is definitely a viable solution, so I'm accepting it. Thanks again.
ChssPly76
A: 

My preference would be to annotate the classes you already have with JAXB annotations and have the binding compiler create the schema based off of the classes you already have by running schemagen against the annotated source files example here.

I've found JAXB to be extremely useful and relativel easy to use in creating schemas from java classes and java classes from schemas that I use in creating web services used for servicing insurance policies here at work.

ChadNC
The link you've provided points to the examples page rather than a concrete example. I've looked through "java-to-schema" ones, but unless I'm missing something this would generate a schema based on _all_ properties defined in API entities rather than their subset. Or, going schema-to-java way I'll have to generate an additional set of classes and copy data to them.
ChssPly76
A: 

Easiest way to go about it: Use a XML serialization API such as XStream or Simple, which are made exactely for this kind of POJO to XML kind of question you are asking. Selecting sub-sets and switching to attributes is easy as the following:

class Customer {
    @XStreamAsAttribute
    String firstName;
    @XStreamAsAttribute
    String lastName;
    @XStreamOmitField
    Address address();
}
Christopher Oezbek
XStream is a serialization library, I am already using it as such. It does not provide a viable data binding approach - for one, fields and getters may not match exactly. Plus, even if it could be adapted as data marshaller that's still only a part of the problem, not an overall solution.
ChssPly76
I agree that XStream is only a serialization library and that there is no way to use the interface instead of a POJO but it is the cleaner way to go (and since you do have them in the background why not use them?). The problem you describe does not need databinding as it is an unidirectional conversion (or I fail to see why it should take a more complicated approach when renaming and omitting fields is well supported)
Christopher Oezbek
Interface is just an API facade; it's irrelevant here. The problem with XStream is that there's no way to use it for _bean_ marshalling. In other words, if I have a `getSomething()` method that's not really an accessor (e.g. it does not have a corresponding `this.something` field but returns some calculated value) I'm out of luck - totally fine for serialization, not so much for marshalling.
ChssPly76