JAXB生成CDATA类型的节点

8 篇文章 0 订阅
5 篇文章 0 订阅

实体:

import com.t3news.doc.biz.xml.CDATAAdapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

/**
 *
 * @author xiaofanku
 */
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement
public class Section {
    /**
     * 索引名称
     */
    @XmlElement
    private String indexName;
    /**
     * 内容
     */
    @XmlJavaTypeAdapter(value=CDATAAdapter.class)
    private String content;
    /**
     * 锚点
     */
    @XmlTransient
    private String[] anchors;
    /**
     * 节标题
     */
    @XmlElement
    private String title;

    //SET/GET

}

测试主类:

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        Section s=new Section();
        s.setTitle("第二季:辣妈自拍上瘾誓做网红");
        s.setContent("<html><p>《四大名助》是一档社交服务爆笑节目。由著名主持人孟非领衔,并搭档三位一线名嘴嘉宾组成的“四大名助”阵容,</p></html>");
        s.setIndexName(StringHelper.randomUUID());

        try {
            Entry.saveSection(s,new File("d://"));
        } catch (JAXBException |IOException ex) {
            Logger.getLogger(TestFile.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

Entry.saveSection方法

    public static void saveSection(Section section, File directory) throws JAXBException, IOException {
        File f=new File(directory,File.separator+section.getIndexName()+".xml");
        if(!f.exists()){
            f.createNewFile();
        }
        toXML(section,Section.class,f);
    }

A: JDK的实现,不需要引入任何jar,主要使用jdk中的rt.jar

    public static <T> void toXML(T obj,Class<?> cls,File output) throws JAXBException{    
        JAXBContext jc = JAXBContext.newInstance(cls);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        //JDK
        marshaller.setProperty("com.sun.xml.internal.bind.characterEscapeHandler", (com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler) (char[] chars, int i, int i1, boolean bln, Writer writer) -> {
            writer.write(chars, i, i1);
        });
        // Output XML
        marshaller.marshal(obj,output);

    }

上面的代码中有个lambda表达式,不过此法至jdk6+都可以

B: JAXB-RI的实现,需要使用以下jar,pom.xml截选

    <repositories>
        <repository>
            <id>central</id>
            <url>http://repo.maven.apache.org/maven2/</url>
        </repository>
    </repositories>
    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <jaxb-version>2.2.11</jaxb-version>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-core -->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>${jaxb-version}</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-xjc</artifactId>
            <version>${jaxb-version}</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.glassfish.jaxb/jaxb-runtime -->
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>${jaxb-version}</version>
        </dependency>

toXML方法:

    public static <T> void toXML(T obj,Class<?> cls,File output) throws JAXBException{    
        JAXBContext jc = JAXBContext.newInstance(cls);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        //JAXB/RI
        //https://jaxb.java.net/nonav/2.2.11/docs/ch05.html
        marshaller.setProperty("com.sun.xml.bind.characterEscapeHandler",new com.sun.xml.bind.marshaller.CharacterEscapeHandler(){
            @Override
            public void escape(char[] chars, int i, int i1, boolean bln, Writer writer) throws IOException {
                writer.write(chars, i, i1);
            }
        });
        // Output XML
        marshaller.marshal(obj,output);
    }

C: 使用eclipselink
pom.xml截选:

    <repositories>
        <repository>
            <id>central</id>
            <url>http://repo.maven.apache.org/maven2/</url>
        </repository>
        <repository>
            <id>EclipseLink</id>
            <url>http://download.eclipse.org/rt/eclipselink/maven.repo</url>
        </repository>
    </repositories>
    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <eclipseLink-version>2.6.4</eclipseLink-version>
        <jaxb-version>2.2.11</jaxb-version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.eclipse.persistence</groupId>
            <artifactId>eclipselink</artifactId>
            <version>${eclipseLink-version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.eclipse.persistence</groupId>
                    <artifactId>commonj.sdo</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

以上(A,B)方法都使用了一个javax.xml.bind.annotation.adapters.XmlAdapter的子类在元素的内容前后包裹

<![CDATA[ 

]]>

,因为marshaller输出时会自动转义实体符号,所以要重写相应的characterEscapeHandler, 如果属性和实现类型不匹配会把CDATA的

<

>

也转义掉同时输出以下错误(红色框住的)

这里写图片描述

以下代码是上面的CDATAAdapter

package com.t3news.doc.biz.xml;

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

/**
 *
 * @author xiaofanku
 */
public class CDATAAdapter extends XmlAdapter<String, String>{
   private static final String CDATA_END = "]]>";
   private static final String CDATA_BEGIN = "<![CDATA[";

    @Override
    public String unmarshal(String v) throws Exception {
     if (v.startsWith(CDATA_BEGIN) && v.endsWith(CDATA_END)) {
       v = v.substring(CDATA_BEGIN.length(), v.length() - CDATA_END.length());
     }
     return v;
    }

    @Override
    public String marshal(String v) throws Exception {
        return CDATA_BEGIN + v + CDATA_END;
    }

}

C.1 eclipselink不需要使用借助XmlJavaTypeAdapter注解,把Section属性content上的注解换成@XmlCDATA,如下:

    /**
     * 内容
     */
    @org.eclipse.persistence.oxm.annotations.XmlCDATA
    private String content;

C.2 Entry.toXML方法如下:

    public static <T> void toXML(T obj,Class[] classes,Map<String, Map<String, StreamSource>> properties,File output) throws JAXBException, IOException{    
        JAXBContext context;

        try (BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(new FileOutputStream(output),"UTF-8"))){

            context = org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(classes, properties);
            Marshaller m = context.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            m.marshal(obj, writer);
        }
    }

注意:
1) 代码中使用了jdk7的TWR特性, 如果jdk小于7换成try..catch..finally
2) TWR中的包装流是为了防止中文乱码,如果你的cdata中全是英文可以不用包装流,直接以下

BufferedWriter writer = new BufferedWriter(new FileWriter(output))

3) 最重要是JAXBContext 要使用org.eclipse.persistence.jaxb.JAXBContextFactory来构造

C.3 saveSection方法如下:

    public static void saveSection(Section section, File directory) throws JAXBException, IOException {
        File f=new File(directory,File.separator+section.getIndexName()+".xml");
        if(!f.exists()){
            f.createNewFile();
        }
        Map<String, StreamSource> oxm = new HashMap<>(1);
        oxm.put("com.t3news.doc.entity", new StreamSource("oxm.xml"));

        Map<String, Map<String, StreamSource>> properties = new HashMap<>();
        properties.put("eclipselink-oxm-xml", oxm);
        toXML(section,new Class[]{Section.class},properties,f);
    }

注意:
1) 如果JAXBContext 还使用

JAXBContext.newInstance(cls)

来构造会提示:eclipselink-oxm-xml属性不支持

2) oxm.xml定义eclipselink的Object xml mapping Metadata,代码

<?xml version="1.0" encoding="UTF-8"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm">
   <java-types>
      <java-type name="com.t3news.doc.entity.Section">
         <java-attributes>
            <xml-element java-attribute="content" cdata="true"/>
         </java-attributes>
      </java-type>
   </java-types>
</xml-bindings>

oxm文件的更详细的参考地址:Bootstrapping from EclipseLink Metadata (OXM)

3) oxm文件的位置,如果放的不对会提示找不到文件

这里写图片描述

解决方案:
1.根据oxm的位置改变StreamSource的构造参数

2.不改代码,把oxm放到classpath下,我用的是netbean8.1,以上代码oxm需要放在这
这里写图片描述

以下是Netbean中看到的截图
这里写图片描述

运行环境:
JDK8 + NetBeans 8.1

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用JAXB生成XML时,如果需要在父节点生成`xmlns:xsi`属性,可以使用`javax.xml.bind.Marshaller.JAXB_SCHEMA_LOCATION`属性来指定XML Schema的位置和命名空间,并通过`javax.xml.bind.Marshaller.setProperty()`方法将该属性设置为要生成的XML中的`xmlns:xsi`属性的值。同时,需要给父节点添加一个新的属性,属性名为`xmlns:xsi`,属性值为`http://www.w3.org/2001/XMLSchema-instance`。例如: ``` JAXBContext jaxbContext = JAXBContext.newInstance(Root.class); Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://example.com/ns path/to/schema.xsd"); Root root = new Root(); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); marshaller.marshal(root, doc); doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); Transformer tf = TransformerFactory.newInstance().newTransformer(); tf.setOutputProperty(OutputKeys.INDENT, "yes"); tf.transform(new DOMSource(doc), new StreamResult(System.out)); ``` 在这个例子中,`Marshaller.JAXB_SCHEMA_LOCATION`属性指定了XML Schema的位置和命名空间。在生成XML时,JAXB会自动将`xsi:schemaLocation`属性添加到生成的XML中,并将其值设置为`http://example.com/ns path/to/schema.xsd`。然后,通过`setAttributeNS()`方法给父节点添加一个新的属性`xmlns:xsi`,属性值为`http://www.w3.org/2001/XMLSchema-instance`。最后,使用`Transformer`将生成的XML格式化输出。例如: ``` <root xmlns="http://example.com/ns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://example.com/ns path/to/schema.xsd"> ... </root> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值