使用MOXy在JAXB中动态CDATA

时间:2013-01-03 18:42:09

标签: jaxb eclipselink cdata moxy

我正在使用EclipseLink JAXB (MOXy)。我有一个问题,java类的属性可以是CDATA或不是动态的。例如,

class Embed{  
//@XmlValue only  
//OR @XmlValue @CDATA  
private String value; //THIS CAN BE CDATA OR NOT  
}

我尝试使用继承来解决问题,其中一个子类将属性作为一个值而另一个子属性作为CDATA。我对这个解决方案的问题是,生成的Xml有xsi:type和xmlns:xsi信息,我不想要,因为我正在升级遗留代码,我需要的结果xml与遗留代码完全相同。

我尝试过的解决方案:

@XmlAccessorType(XmlAccessType.FIELD)  
@XmlType(name = "itemType", namespace = "", propOrder = {"embed"}  
public class Item{  
 List`<Embed>` embed;  
 //getter and setters  
}

@XmlAccessorType(XmlAccessType.NONE)  
@XmlSeeAlso(EmbedDefault.class, EmbedAsCdata.class)  
public abstract class Embed{

}

@XmlAccessorType(XmlAccessType.FIELD)    
@XmlType( propOrder = {"value"})  
public class EmbedDefault extends Embed{

@XmlValue
protected String value;
 //getters and setters  
}

@XmlAccessorType(XmlAccessType.FIELD)  
@XmlType(propOrder = {"value"})  
public class EmbedAsCdata extends Embed {  

@XmlValue
@XmlCDATA
protected String value;
//getters and setters  
}

还有其他更简单的方法吗?

1 个答案:

答案 0 :(得分:1)

注意:我是EclipseLink JAXB (MOXy)主管,是JAXB (JSR-222)专家组的成员。

我正在尝试为您提供一个不错的解决方法,但下面是一个增强请求,您可以按照我们要增强的@XmlCDATA注释来使这个用例更容易支持。< / p>


更新#1

以下是我正在为您的用例工作的方法。它需要对MOXy进行一些小改动,我已经想到但仍需要进行全面测试。您可以使用以下链接跟踪我们在此问题上的进展:

DomHandler(CdataHandler)

JAXB具有DomHandler的概念,允许您对XML的外观进行一些额外的控制。我们将在需要时利用DomHandler添加CDATA块。

package forum14145131;

import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.parsers.*;
import javax.xml.transform.Source;
import javax.xml.transform.dom.*;

import org.w3c.dom.*;

public class CdataHandler implements DomHandler<String, DOMResult> {

    private static DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();;

    @Override
    public DOMResult createUnmarshaller(ValidationEventHandler veh) {
        return new DOMResult();
    }

    @Override
    public String getElement(DOMResult domResult) {
        Document document = (Document) domResult.getNode();
        return document.getDocumentElement().getTextContent();
    }

    @Override
    public Source marshal(String string, ValidationEventHandler veh) {
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document document = db.newDocument();

            Node node;
            if(string.contains("<") || string.contains("&") || string.contains("&")) {
                node = document.createCDATASection(string);
            } else {
                node = document.createTextNode(string);
            }
            return new DOMSource(node);
        } catch(Exception e) {
            throw new RuntimeException(e);
        }
    }

}

<强>嵌入

@XmlAnyElement注释用于指定DomHandler

package forum14145131;

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Embed{  

    @XmlAnyElement(CdataHandler.class)
    private String value; //THIS CAN BE CDATA OR NOT  

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

}

了解更多信息


更新#2

以下是使用现有EclipseLink库的方法,利用XmlAdapterCharacterEscapeHandler

XmlAdapter(CdataAdapter)

package forum14145131;

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

public class CdataAdapter extends XmlAdapter<String, String> {

    @Override
    public String marshal(String string) throws Exception {
        if(string.contains("&") || string.contains("<") || string.contains("\"")) {
            return "<![CDATA[" + string + "]]>";
        } else {
            return string;
        }
    }
    @Override
    public String unmarshal(String string) throws Exception {
        return string;
    }

}

<强>嵌入

@XmlJavaTypeAdapter注释用于指定XmlAdapter

package forum14145131;

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Embed{  

    @XmlValue
    @XmlJavaTypeAdapter(CdataAdapter.class)
    private String value; //THIS CAN BE CDATA OR NOT  

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

}

<强>演示

以下是指定CharacterEscapeHandler的方式。请注意,这会覆盖此Marshaller转义的所有字符。我们这样做是为了CDATA部分不会被转义。对于生产代码,您需要加强此方法的实现。

package forum14145131;

import java.io.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.oxm.CharacterEscapeHandler;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Embed.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum14145131/input.xml");
        Embed embed = (Embed) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(MarshallerProperties.CHARACTER_ESCAPE_HANDLER,
                new CharacterEscapeHandler() {
                    @Override
                    public void escape(char[] ac, int i, int j, boolean flag,
                            Writer writer) throws IOException {
                        writer.write(ac, i, j);
                    }
                });
        marshaller.marshal(embed, System.out);
    }

}

<强> input.xml中/输出

<?xml version="1.0" encoding="UTF-8"?>
<embed><![CDATA[Hello & World]]></embed>