Showing posts with label JAXB. Show all posts
Showing posts with label JAXB. Show all posts

Tuesday, July 6, 2010

JAXB, Sun and how to marshal a CDATA element

According to https://jaxb.dev.java.net/faq/index.html#marshalling_cdata there is no direct support in marshalling CDATA blocks, this is vendor specific.
I'll describe how this is done when using the built-in Sun implementation by an example.


Suppose this is my JAXB annotated class:
package org.oded;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Item
{
 @XmlAttribute public int id;
 
 public String text;
}

Next I run the Main class:
package org.oded;

import java.io.*;

import javax.xml.bind.*;

import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;

public class Main
{
 public static void main( String[] args ) throws Exception
 {
  Item i1 = new Item();
  i1.text = "hello";
  i1.id = 1;
  
  Item i2 = new Item();
  i2.text = "";
  i2.id = 2;
    
  Marshaller m = JAXBContext.newInstance( Item.class ).createMarshaller();
  
  m.marshal( i1, new OutputStreamWriter( System.out ) );
  System.out.println();
  m.marshal( i2, new OutputStreamWriter( System.out ) );
 }
}

The output is:
hello
<code><helloWorld/></code>

Now suppose I want to wrap the XML value of text in a CDATA element and avoid the escaping, I need to add specify an Adapter for text to surround the value in a CDATA element in the following way:

package org.oded;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Item
{
 @XmlAttribute public int id;
 
 @XmlJavaTypeAdapter(value=Adapter.class) 
 public String text;
 
 private static class Adapter extends XmlAdapter
 {

  @Override
  public String marshal( String v ) throws Exception
  {
   return "<![CDATA[" + v + "]]>";
  }

  @Override
  public String unmarshal( String v ) throws Exception
  {
   return v;
  }
  
 }
}

Now tell the Marshaller, in a Sun-specific way, not to escape the value of text:
package org.oded;

import java.io.*;

import javax.xml.bind.*;

import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;

public class Main
{
 public static void main( String[] args ) throws Exception
 {
  Item i1 = new Item();
  i1.text = "hello";
  i1.id = 1;
  
  Item i2 = new Item();
  i2.text = "";
  i2.id = 2;
    
  Marshaller m = JAXBContext.newInstance( Item.class ).createMarshaller();
  m.setProperty( "com.sun.xml.internal.bind.characterEscapeHandler", new CharacterEscapeHandler() {
   @Override
   public void escape( char[] ac, int i, int j, boolean flag, Writer writer ) throws IOException
   {
    // do not escape
    writer.write( ac, i, j );
   }
  });
  
  m.marshal( i1, new OutputStreamWriter( System.out ) );
  System.out.println();
  m.marshal( i2, new OutputStreamWriter( System.out ) );
 }
}

Now the output is:
<![CDATA[hello]]>
<![CDATA[]]>