2

我正在使用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  
}

还有这种更简单的方法吗?

4

1 回答 1

1

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

I'm trying to come up with a decent workaround for you, but below is an enhancement request you can follow where we are going to enhance the @XmlCDATA annotation to make this use case much easier to support.


UPDATE #1

Below is an approach I'm working on for your use case. It requires a small change to MOXy which I have figured out but still need to fully test. You can track our progress on this issue using the following link:

DomHandler (CdataHandler)

JAXB has the concept of a DomHandler that allows you some extra control of what the XML looks like. We will leverage a DomHandler to add in a CDATA block when required.

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);
        }
    }

}

Embed

The @XmlAnyElement annotation is used to specify the 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;
    }

}

For More Information


UPDATE #2

Below is an approach that you can use today using the existing EclipseLink library leveraging an XmlAdapter and a CharacterEscapeHandler:

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;
    }

}

Embed

The @XmlJavaTypeAdapter annotation is used to specify the 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;
    }

}

Demo

Below is how you specify the CharacterEscapeHandler. Note this overrides all the character escaping for this Marshaller. We do this so the CDATA portion doesn't get escaped. For production code your implementation of this method would need to be beefed up over what is presented in this example.

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/Output

<?xml version="1.0" encoding="UTF-8"?>
<embed><![CDATA[Hello & World]]></embed>
于 2013-01-05T11:51:45.867 回答