Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force escaping Special character in XML Marshalling in Spring

I want to force escaping special characters when I use Spring Marshaller. Below code is perfectly working when I use javax.xml.bind.Marshaller

Book Class

package com.odr.core.action;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "book")
public class Book {

    private String name;
    private String author;
    private String publisher;
    private String isbn;

    @XmlJavaTypeAdapter(value=CDATAAdapter.class)
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Book [name=" + name + ", author=" + author + ", publisher="
            + publisher + ", isbn=" + isbn + ", description=" + description
            + "]";
    }   
}

Object to XML

        writer = new BufferedWriter(new FileWriter(selectedFile));
        context = JAXBContext.newInstance(Book.class);
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        m.setProperty("com.sun.xml.bind.marshaller.CharacterEscapeHandler",
                new CharacterEscapeHandler() {
                    @Override
                    public void escape(char[] ch, int start, int length,
                            boolean isAttVal, Writer writer)
                            throws IOException {
                        writer.write(ch, start, length);
                    }
                });
        m.marshal(book, writer);

Output:

<description>

<![CDATA[<p>With hundreds of practice questions and hands-on exercises, <b>SCJP Sun Certified Programmer for Java 6 Study Guide</b> covers what you need to know--and shows you how to prepare--for this challenging exam. </p>]]>
</description>

But same kind of code is not working when I use org.springframework.oxm.jaxb.Jaxb2Marshaller, Below is the code

    Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("jaxb.formatted.output", true);
    jaxb2Marshaller.setPackagesToScan("com.odr.core.action");
    // com.sun.xml.bind.characterEscapeHandler
    // com.sun.xml.bind.marshaller.CharacterEscapeHandler
    map.put("com.sun.xml.bind.marshaller.CharacterEscapeHandler",
            new CharacterEscapeHandler() {
                @Override
                public void escape(char[] ac, int i, int j, boolean flag,
                        Writer writer) throws IOException {
                    writer.write(ac, i, j);
                }
            });
    jaxb2Marshaller.setMarshallerProperties(map);

    org.springframework.oxm.Marshaller marshaller = jaxb2Marshaller;
    FileOutputStream fos = null;
    // String fileNamePath = directory.getAbsolutePath() + "\\" + fileName;

    try {
        // fos = new FileOutputStream(fileNamePath);
        fos = new FileOutputStream(selectedFile);
        marshaller.marshal(book, new StreamResult(fos));

        // File f = new File(directory,fileName);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            fos.close();
        }
    }

Output

<description>&lt;![CDATA[&lt;p&gt;With hundreds of practice questions and hands-on exercises, &lt;b&gt;SCJP Sun Certified Programmer for Java 6 Study Guide&lt;/b&gt; covers what you need to know--and shows you how to prepare--for this challenging exam. &lt;/p&gt;]]&gt;</description>

The first snippet didn't encode the special characters. But the second snippet which is using Spring did encode though I set property. I have to use Spring in my project for not affecting existing code. Is there any way I can fix it

like image 322
Selvakumar Ponnusamy Avatar asked Jul 14 '16 18:07

Selvakumar Ponnusamy


People also ask

What is marshalling in XML?

Object/XML Mapping, or O/X mapping for short, is the act of converting an XML document to and from an object. This conversion process is also known as XML Marshalling, or XML Serialization.

What is marshalling in spring?

As stated in the introduction, a marshaller serializes an object to XML, and an unmarshaller deserializes XML stream to an object. In this section, we will describe the two Spring interfaces used for this purpose.

Which annotation is used to Map a Java class object to XML element?

The JAXB annotations defined in the javax. xml. bind. annotations package can be used to customize Java program elements to XML schema mapping.


1 Answers

Ok, I had the same problem, and I solved it this way.

First things first. You should create two beans. One for the Jaxb2Marshaller and another MarshallingHttpMessageConverter. I'm supposing that you want to keep your configuration, so I'm going to use your code.

Creating the Jaxb2Marshaller bean:

@Bean
public Jaxb2Marshaller getJaxb2Marshaller() {
    Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
    Map<String, Object> map = new HashMap<String, Object>();
    map.put("jaxb.formatted.output", true);
    jaxb2Marshaller.setPackagesToScan("com.odr.core.action");
    map.put("com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler",
            new CharacterEscapeHandler() {
                @Override
                public void escape(char[] ac, int i, int j, boolean flag,
                                   Writer writer) throws IOException {
                    writer.write(ac, i, j);
                }
            });
    jaxb2Marshaller.setMarshallerProperties(map);

    org.springframework.oxm.Marshaller marshaller = jaxb2Marshaller;
    FileOutputStream fos = null;

    try {
        fos = new FileOutputStream(selectedFile);
        marshaller.marshal(book, new StreamResult(fos));
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            fos.close();
        }
    }

    return jaxb2Marshaller;
}

Well, I'm using Java 8 so I changed com.sun.xml.bind.marshaller.CharacterEscapeHandler to com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler as you can see above.

Creating the MarshallingHttpMessageConverter bean:

@Bean
public MarshallingHttpMessageConverter getMarshallingHttpMessageConverter() {
    return new MarshallingHttpMessageConverter(getJaxb2Marshaller());
}

You must notice that I've created my own HttpMessageConverter to solve the problem. That's because Spring uses it's own converter that creates a new Marshaller instance everytime it needs to convert a entity or a DTO to XML objetct. So, I think the code below will solve your problem. Hope it helps you.

import com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;

@Configuration
public class XmlParseConfig {
    @Bean
    public Jaxb2Marshaller getJaxb2Marshaller() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("jaxb.formatted.output", true);
        jaxb2Marshaller.setPackagesToScan("com.odr.core.action");
        map.put("com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler",
                new CharacterEscapeHandler() {
                    @Override
                    public void escape(char[] ac, int i, int j, boolean flag,
                                       Writer writer) throws IOException {
                        writer.write(ac, i, j);
                    }
                });
        jaxb2Marshaller.setMarshallerProperties(map);

        org.springframework.oxm.Marshaller marshaller = jaxb2Marshaller;
        FileOutputStream fos = null;

        try {
            fos = new FileOutputStream(selectedFile);
            marshaller.marshal(book, new StreamResult(fos));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                fos.close();
            }
        }

        return jaxb2Marshaller;
    }

    @Bean
    public MarshallingHttpMessageConverter getMarshallingHttpMessageConverter() {
        return new MarshallingHttpMessageConverter(getJaxb2Marshaller());
    }
}
like image 177
R. Karlus Avatar answered Oct 22 '22 17:10

R. Karlus