实体:
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