【问题标题】:Dynamic CDATA in JAXB using MOXy使用 MOXy 的 JAXB 中的动态 CDATA
【发布时间】:2012-12-18 04:22:56
【问题描述】:

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

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

【问题讨论】:

    标签: jaxb eclipselink cdata moxy


    【解决方案1】:

    注意:我是EclipseLink JAXB (MOXy) 的负责人,也是JAXB (JSR-222) 专家组的成员。

    我正在尝试为您提供一个体面的解决方法,但您可以遵循以下增强请求,我们将增强 @XmlCDATA 注释以使此用例更易于支持。


    更新 #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/Output

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

    【讨论】:

    • 感谢您的回复。可能您已经考虑过了,但是考虑到上述问题,建议的增强解决方案仍然存在相同的问题。即使我们设置了@XmlCDATA(XmlCDATAType.Optional),问题仍然存在,即如何动态识别该值是否为CDATA。或者,当使用 @XmlCDATA(XmlCDATAType.Optional) 时,您对如何决定是否呈现 CDATA 有一些想法。再次感谢。
    • @user1946448 - 没问题。我已经用另一种方法更新了我的答案,但它需要对 MOXy 进行一些小改动,我仍然需要对其进行全面测试。为了确定 CDATA 块的存在,我只是要检查 &lt;&amp;" 的存在,这对于您的用例是否足够?
    • 衷心感谢您的更新。对于更新 #2,CharacterEscapeHandler 不会转义所有字符。可能这就是你说它需要“加强”的原因。你认为这并没有逃避所有角色,这可能是一个问题吗?如果是这样,我们可以使用一些默认的转义模式并修改它以排除 cdata
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 2014-05-15
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多