9

I need to serialize an XML java object to a XML file using the JAXB Marshaller (JAXB version 2.2). Now in the xml object, I have a tag which contains String value such that:

"<"tagA>
**"<"YYYYY>done"<"/YYYYY>**
"<"/tagA>

Now as you can see that this string value again contains tags. I want this to be written in the same way in the xml file.

But JAXB Marshaller converts these values such as:

"&"lt;YYYYY"&"gt;"&"#xD;done ...& so on

I am not able to treat these escape characters separately using JAXB 2.2 Is it possible anyways?

Any help in this regard will be great..

Thanks in advance, Abhinav Mishra

javdev
  • 794
  • 2
  • 10
  • 23

5 Answers5

11

Done it by setting the following property for the JAXB Marshaller:

marshaller.setProperty("jaxb.encoding", "Unicode");
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
javdev
  • 794
  • 2
  • 10
  • 23
  • can you please elaborate on your usage to escape the charatcer. I am not able to do that with jus the property set to unicode. –  Jan 04 '11 at 05:17
  • 1
    To elaborate the same, I had just set the following property to the marshaller: marshaller.setProperty("jaxb.encoding", "Unicode"); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CustomCharacterEscapeHandler()); – javdev Jan 05 '11 at 04:08
  • Here in the third statement as you can see I had created a CustomCharacterEscapeHandler class which is actually doing the escape character handling. You can find its code easily on the net by searching 'how to override the escape method of CharacterEscapeHandler'. In case you need the code then ask me.. – javdev Jan 05 '11 at 04:12
9

There is one simpler way. First use custom escape sequence:

m.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() {
    @Override
    public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
        out.write( ch, start, length ); 
    }
}); 

Then marshal it to a String like mentioned below

StringWriter writer = new StringWriter();
m.marshal(marshalObject, writer);

and then create a document object from the writer mentioned below

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource is = new InputSource( new StringReader( writer.toString() ) );
Document doc = builder.parse( is );

escape characters issue will be resolved

Matthias
  • 7,432
  • 6
  • 55
  • 88
preetham
  • 91
  • 1
  • 2
  • 5
    the problem of this approach is that you can not build the app with OpenJDK. com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler belongs to Sun JDK. – walv Oct 26 '16 at 14:21
  • You can use com.sun.xml.bind.marshaller.CharacterEscapeHandler – user2814648 Jan 24 '18 at 16:50
  • @user2814648 com.sun.xml.bind.marshaller.CharacterEscapeHandler isn't on the classpath. At least, not for me. What library is it in? – Hakanai Aug 13 '18 at 01:39
3

With JAXB marshaller if you want full control over which characters to escape(e.g. "\'") you will have to add property :

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CustomCharacterEscapeHandler());

and create a new CustomCharacterEscapeHandler class

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

import java.io.IOException;
import java.io.Writer;

public class CustomCharacterEscapeHandler implements CharacterEscapeHandler {

    public CustomCharacterEscapeHandler() {
        super();
    }

    public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException {
        // avoid calling the Writerwrite method too much by assuming
        // that the escaping occurs rarely.
        // profiling revealed that this is faster than the naive code.
        int limit = start+length;
        for (int i = start; i < limit; i++) {
            char c = ch[i];
            if(c == '&' || c == '<' || c == '>' || c == '\'' || (c == '\"' && isAttVal) ) {
                if(i!=start)
                    out.write(ch,start,i-start);
                start = i+1;
                switch (ch[i]) {
                    case '&':
                        out.write("&amp;");
                        break;
                    case '<':
                        out.write("&lt;");
                        break;
                    case '>':
                        out.write("&gt;");
                        break;
                    case '\"':
                        out.write("&quot;");
                        break;
                    case '\'':
                        out.write("&apos;");
                        break;
                }
            }
        }

        if( start!=limit )
            out.write(ch,start,limit-start);
    }
}

Hope that helps.

Patrik Bego
  • 4,009
  • 1
  • 26
  • 24
2

You can leverage the CDATA structure. Standard JAXB does not cover this structure. There is an extension in EclipseLink JAXB (MOXy) for this (I'm the tech lead). Check out my answer to a related question:

It describes the @XmlCDATA annotation in MOXy:

import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlCDATA;

@XmlRootElement(name="c")
public class Customer {

   private String bio;

   @XmlCDATA
   public void setBio(String bio) {
      this.bio = bio;
   }

   public String getBio() {
      return bio;
   }

}

For more information see:

Community
  • 1
  • 1
bdoughan
  • 147,609
  • 23
  • 300
  • 400
  • Thanks for your reply Blaise. I'll explore this one for sure as it seems interesting. However, I have found a solution for my problem which I am posting below.. – javdev Dec 16 '10 at 04:33
1

Depending on what you are exactly looking for you can either :

  • disable character escaping
  • or use CDATA string which support can be added into JAXB with just a bit of configuration
fred
  • 9
  • 1
fred
  • 75
  • 1
  • 1