Alex Rivera | Logout

JAXB marshalling purely from interfaces

Asked 2012-03-06T18:33:15.450
17

I have a complex hierarchy of Java interfaces that I'd like to marshal (and not necessarily unmarshal) with JAXB. These interfaces represent objects that will be returned from a JAX-RS REST API as XML, JSON, YAML, etc. (I'm using RestEasy, which can marshal JAXB-annotated types in formats other than XML.)

The problem seems to be that JAXB is fundamentally class-oriented. I've done lots of web research on the difficulties with JAXB and interfaces, the closest solutions being MOXy JAXB - Map Interfaces to XML and JAXB and Interface Fronted Models. However, I have two main issues: a) I want to annotate/work in terms of interfaces, not the concrete classes (of which there will be multiple implementations and containing significant other state that should not be marshaled), and b) I have multiple levels of interface inheritance. Here's a sample of the interfaces, minus any JAXB annotations so far:

interface Uuided {
  UUID getId();
}
interface Named {
  String getName();
}
interface Component extends Uuided, Named {
  Map<String, ComponentAttribute> getAttributes();
}
interface Attribute extends Named {
  Type getType();
  Object getValue();
}
interface ComponentAttribute extends Attribute {
  Component getDeclaringComponent();
}

Ideally, this would generate something like:

<component id="xxx" name="thing">
  <attributes>
    <componentAttribute name="color">
      <type><stringType/></type>
      <value>green</value>
      <declaringComponent idref="xxx"/>
    </componentAttribute>
  </attributes>
</component>

Clearly, in the abstract, this leads to issues like determining the most derived annotated interface, of which there could theoretically be m

Edit
Report

1 Answer

1

I think the answer is that JAXB isn't at all intended to support this and that it's foolish to try to force it. Also, JAXB-driven JSON marshaling turns out not to be ideal either.

I ended up writing my own marshaling framework, with its own set of annotations:

@MarshalMixin // marshal fields but not a top-level object
interface Uuided {
  @MarshalAsString // ignore properties; just call toString()
  @MarshalId // treat as identifier for @MarshalUsingIds or cyclic ref
  UUID getId();
}
@MarshalMixin
interface Named {
  @MarshalId
  String getName();
}
@MarshalObject // top-level marshaled object providing class name
interface Component extends Uuided, Named {
  @MarshalAsKeyedObjectMap(key = "name") // see description below
  Map<String, ComponentAttribute> getAttributes();
}
@MarshalObject
interface Attribute extends Named {
  Type getType();
  @MarshalDynamic // use run-time (not declared) type
  Object getValue();
}
interface ComponentAttribute extends Attribute {
  @MarshalUsingIds
  Component getDeclaringComponent();
}

The generated marshalers write to an abstraction layer (currently implemented for JSON and XML). This gives a lot more flexibility to make the output natural for different representations without a ton of annotations and adapters. E.g., what I'm calling keyed-object maps, where each object contains its map key. In JSON, you want a map, but in XML, you want a sequence:

{..., map: {'a': {'name': 'a', ...}, 'b': {'name: 'b', ...}, ...}, ...}
...<map><thing name='a'>...</thing><thing name='b'>...</thing></map>...

Seems like as many as 4 other people care about this too, so hopefully I can open-source it eventually. :-)

answered 2012-04-10T16:53:43.637

Your Answer