views:

2277

answers:

3

Hi,

Is there a way to customize XML serialization in JAXB, in the same way that it's possible using IXmlSerializable in .NET? (i.e. the ability to directly control serialization of an object using the equivalent of an XmlReader/Writer).

I've taken a look at XmlAdapter and @XmlJavaTypeAdapter, but they just seem to be used to transform types to and from serializable forms, which isn't quite what I want.

Update: In particular, I'd like to customize the deserialization of a root object, that determines, programatically, exactly how to deserialize the inner XML (e.g. create a jaxb unmarshaller with a particular set of known types).

Update: I've found a way to solve the problem, but it's such a nasty hack I'll probably go with one of the solutions suggested by the other posters.

+1  A: 

I'm not sure I fully understand what your goal is, but maybe you could do what you want to do programatically inside the no-args constructor of your root object, which will be called to instantiate the object when it is unmarshalled.

Fabian Steeg
+1  A: 

Sounds like you want a root node to contain two different sets of xml nodes/trees based on some sort of attribute or knowledge gained from the root node itself.

I tend to use JAXB by first creating a schema (xsd) and then generating the Java source, which I NEVER modify. I only modify the schema and regen. I realized that JAXB can't be used the other way, by annotating source, buy I can't speak to that way since I don't use it. If you you a schema ... read on

Consider this: http://www.w3schools.com/Schema/schema_complex_indicators.asp

Rather than switching the unmarshaller, just include both xml definitions. They will be get parsed regardless and you can vary you program control flow via whatever attribute you would check on the root node.

basszero
You nailed my problem - spot on. Definitely agree about the XSD, but I haven't marked this as accepted yet, as I think there may be a way to solve my problem by hacking around with XmlAdapters. We'll see...
Andy
+1  A: 

OK, so I managed to get this working, although it's such a nasty solution that I think I'll find a higher-level way of working around the problem, as Fabian and basszero have mentioned. The idea of the following code is to create a generic serializable reference to the data you want to serialize, which maintains a JAXB java type adapter to perform the serialization programmatically, and a string field that stores the resulting XML.

Note: Code has been greatly simplified for display...

// Create an instance of this class, to wrap up whatever you want to custom-serialize
@XmlRootElement
public static class SRef
{
 public SRef() { }
 public SRef(Object ref)
 {
  this.ref = ref;
 }

 @XmlJavaTypeAdapter(SRefAdapter.class)
 public Object ref;
}

// This is the adapted class that is actually serialized 
public static class SRefData
{
 // This is a hint field to inform the adapter how to deserialize the xmlData
 @XmlAttribute
 public String hint;

 // This contains the custom-serialized object
 @XmlElement
 public String xmlData;
}

    // Converts an object to and from XML using a custom serialization routine
public static class SRefAdapter extends XmlAdapter<SRefData, Object>
{
 @Override
 public SRefData marshal(Object value) throws Exception
 {
  if (value instanceof MyType)
  {
   SRefData data = new SRefData();
   data.xmlData = doSomeSpecificSerialization(value);
   data.hint = "myType";
   return data;
  }
  throw new IllegalArgumentException("Can't serialize unknown object type " + value.getClass());
 }

 @Override
 public Object unmarshal(SRefData refData) throws Exception
 {
  if (refData.hint.equals("myType"))
  {
   return doSomeSpecificDeserialization(refData.xmlData);
  }
  throw new IllegalArgumentException("Unhandled hint value in SRefData: " + refData.hint); 
 }
}
Andy