3

How to append externally generated XML instead of variable value of POJO using JAXB?

I have specific data structure as follows:

Map<CustomEnum, CustomList>

Until now, I used utility class to generate xml representation of data in this structure. It uses SAX to generate the xml output.

For further reading, CustomEnum is java enum, CustomList extends java.util.List, its implementation is obtained through factory methods. It allows to contain only objects implementing specific interface. Their implementations are obtained through factory methods too, mostly based on specific conditions. I have no access to modify any of those classes.

This is why transforming of this structure through jaxb seems quite complicated to me, but there are many other reasons, why that utility class was writen (there is lot of conditional evaluating, for example if some values are not null, get data from other values, otherwise provide default values, etc.)

Now this data structure needs to be included as part of bigger structure, based on POJOs and transformed into xml using jaxb.

Something like this:

public class CustomPojo {
  ...
  private String data;
  ...
  private Map<CustomEnum, CustomList> items;
}

XML output should be:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rootElement>
  <data> value </data>
  ...

  <items>  
    <!-- generated xml from my utility class -->
  </items>
</rootElement>

What I need is, wherever jaxb marshaller finds member variable of pojo of type Map, instead of trying to generate xml from that variable, to insert xml generated by my utility class.

So I came up with idea to implement custom adapter:

public class CustomAdapter extends XmlAdapter<String, Map<CustomEnum, CustomList>> 

using it as

  ...
  @XmlJavaTypeAdapter(CustomAdapter.class)
  private Map<CustomEnum, CustomList> items;

and in overriden marshal method let return xml generated by my utility.

but it gives similar output to this:

<items>&#xD;&lt;item itemType="1"&gt;&#xD; ...

So as I can see, it is escaped string with xml data instead of xml tags tree.

My question is: Is there way to tell jaxb not to generate xml from property value but insert exterally generated xml as part of its own xml instead?

P.S. Sorry for long description, but I wanted to make a picture as much clear as possible. And please note, that I am aware of that this design is not ideal, but I need this to be working. I am planning general refactoring, but it seems to be a long shot.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Jiri Kusa
  • 473
  • 6
  • 16
  • There is the same question asked in this thread: http://stackoverflow.com/questions/1506663/can-i-force-jaxb-not-to-convert-into-quot-for-example-when-marshalling-to Hope that helps! Good luck! – jlr Jan 14 '14 at 15:33
  • @jlr Thanks for your reply. But If I can see correctly, it is concerned to escaping text value of xml tag. But I need that text value to be xml structure (xml tags) and not one string value containing that xml structure encoded. – Jiri Kusa Jan 14 '14 at 15:38
  • 1
    Unless I misunderstand your issue, you need to override the default character escape that produces the result you have, so that it does not do any encoding. So you need to override the CharacterEscapeHandler class that will basically do nothing. In this implementation, you will **not** escape the characters but simply writes those characters. Fred has the simplest and most direct implementation in the link I provided. – jlr Jan 14 '14 at 15:54
  • @jlr Sorry for delay, your suggestion is correct. Using solution of Fred gives me desired output. Can you please put your comments into an answer, so I could accept it? – Jiri Kusa Jan 15 '14 at 09:40
  • Yes sure! Thanks! And glad it worked out for you! – jlr Jan 15 '14 at 15:34

1 Answers1

2

By default, the marshaller implementation of JAXB escapes all characters, even for custom adapters (as they also go through the same marshaller). The solution to prevent character escape is to override the default mechanism. So we need to provide a custom implementation of CharacterEscapeHandler that will absolutely do nothing in terms of characters escaping. Instructions on how to do so are given in the JAXB documentation:

  1. Write a class that implements the com.sun.xml.bind.marshaller.CharacterEscapeHandler interface.
  2. Create a new instance of it.
  3. Set that instance to the Marshaller by using this property (i.e. com.sun.xml.bind.characterEscapeHandler).

Hence, such implementation would look like...

public class NullCharacterEscapeHandler implements CharacterEscapeHandler {

  public NullCharacterEscapeHandler() {
    super();
  }

  public void escape(char[] ch, int start, int length, boolean isAttVal, Writer writer) throws IOException {
    // Proxy the characters to the writer, with no encoding escape.
    writer.write( ch, start, length );
  }
}

...and used this way:

Marshaller m = jcb.createMarshaller();
m.setProperty("com.sun.xml.bind.marshaller.CharacterEscapeHandler",
              new NullCharacterEscapeHandler());

This reply was greatly inspired by this stackoverflow thread.

Community
  • 1
  • 1
jlr
  • 1,362
  • 10
  • 17