package me.test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.xml.sax.InputSource;
import org.xml.sax.XMLReader;
import com.ibm.icu.util.Calendar;
/**
* 使用JAXB(JSR 222)解析、生成XML文件(大数据量)。
*
* 这里使用先编写XSD,再生成JavaBean的方式(类似于WebService中的Contract First-契约优先,或Up-Down方式)
* 其实也可以先写JavaBean,再由Javabean生成XSD。
*
* Step 1. 使用Eclipse图形编写需要的XSD。
* Step 2. 使用 JDK 6 自带的 xjc 命令自动生成javaBean代码。
* (如果使用既有的XSD文件,且命名空间不符合要求,可以参考使用 external bindings files (*.xjb))
* (如果使用JDK 1.5 单独下载 JAXB的RI即可,JAXB不支持JDK 1.4。)
* Step 3. 使用生成的JavaBean解析、生成XML。
*
* 参考:
* JSR 222
*
* JAXB RI(Reference Implementation, 参考实现)
* https://jaxb.dev.java.net/
* JAXB RI 中的 例子
* JAXB2_20090708.jar!samples/partial-unmarshalling/*
* XJC 命令介绍
*
* Customizing JAXB Bindings
*
*
* @author btpka3@163.com
*/
public class JAXBTest {
// XML -> JavaBean (unmarshaller)
public static void unmarshall() throws Exception {
JAXBContext context = JAXBContext.newInstance("me.test"); //package名称
Unmarshaller unmarshaller = context.createUnmarshaller();
// 创建并设定一个 Unmarshaller.Listener
ExamUnmarshallListener unmarshallListener = new ExamUnmarshallListener();
unmarshaller.setListener(unmarshallListener);
// create a new XML parser
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
XMLReader reader = factory.newSAXParser().getXMLReader();
// 流式解析XML,主要使用了该方法。
// 缺点就是造成了业务逻辑代码片段被分割成一个一个的片段,而无法连续。
// FIXME : 如何使用 StAX + JAXB? 且同时做到代码尽可能的简单?
reader.setContentHandler(unmarshaller.getUnmarshallerHandler());
try {
reader.parse(new InputSource(
JAXBTest.class.getResourceAsStream("exam.xml")));
} catch (Exception e) {
// 如果觉得将业务异常变成RuntimeExcepiton不合适的话,
// 就应该在 ExamUnmarshallListener 中设定结果正确标志,
// 如果正确,就取得正确结果,否则取得异常信息。
System.out.println("捕获了异常 :" + e.getMessage());
e.printStackTrace();
}
}
// JavaBean -> XML (marshall)
public static void marshall() throws JAXBException, XMLStreamException,
FileNotFoundException, UnsupportedEncodingException {
// Step 1. 创建JAXB上下文
JAXBContext context = JAXBContext.newInstance(ExamType.class);
// Step 2. 创建Marshaller
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK");
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, false);
//marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "");
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
// Step 3. 使用StAX写入开始标签和基本信息。
XmlSchema xmlSchema = PersonType.class.getPackage().getAnnotation(
XmlSchema.class);
String ns = xmlSchema.namespace();
System.out.println(xmlSchema.namespace());
ObjectFactory fac = new ObjectFactory();
XMLOutputFactory output = XMLOutputFactory.newInstance();
output.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
File file = new File("C:/aa.txt");
OutputStream out = new FileOutputStream(file);
XMLStreamWriter writer = output.createXMLStreamWriter(out, "UTF-8");
writer.writeStartDocument();
writer.setDefaultNamespace(ns);
writer.writeStartElement(ns, "exam");
// Step 4. 使用JaXB写入循环的大量记录
for (int i = 0; i < 3; i++) {
// Step 4.1. 准备需要的JavaBean(比如查询数据库)
PersonType p = new PersonType();
p.setId("04111010" + (i + 1));
p.setName("Name" + (i + 1));
p.setSex("男");
Calendar c = Calendar.getInstance();
c.set(1985, 7, i + 1, 0, 0, 0);
p.setBirthday(fromDate(c.getTime()));
for (int j = 0; j < 3; j++) {
ScoreType score = new ScoreType();
score.setSubject("s" + (j + 1));
score.setValue(85 + j);
p.getScore().add(score);
}
JAXBElement<PersonType> personElement = fac.createPerson(p);
// 计算当前记录转换成XML后的长度(注意:会)
StringWriter w = new StringWriter();
marshaller.marshal(personElement, w);
int curRecLength = w.getBuffer().length();
System.out.println("当前记录的长度为(含xmlns):" + curRecLength);
System.out.println(w);
// Step 4. 转换并输出
marshaller.marshal(personElement, writer);
writer.flush();
System.out.println("XML 文件当前记录长度 (仅根元素含xmlns)= " + file.length());
}
// 写入结束标签
writer.writeEndElement();
writer.writeEndDocument();
writer.flush();
}
public static void main(String[] args) {
try {
unmarshall();
} catch (Exception e) {
e.printStackTrace();
}
try {
marshall();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String personToString(PersonType p) {
StringBuilder buf = new StringBuilder();
buf.append("\n----------------person");
buf.append("\n id = " + p.getId());
buf.append("\n name = " + p.getName());
buf.append("\n sex = " + p.getSex());
buf.append("\n birthday = " + p.getBirthday());
for (ScoreType s : p.getScore()) {
buf.append("\n score{" + s.getSubject() + " : " + s.getValue()
+ "}");
}
return buf.toString();
}
// XMLGregorianCalendar -> Date
public static Date toDate(XMLGregorianCalendar xcal) {
Date dt = xcal.toGregorianCalendar().getTime();
//java.sql.Date sqlDt = new java.sql.Date(dt.getTime());
return dt;
}
// Date -> XMLGregorianCalendar
public static XMLGregorianCalendar fromDate(Date date) {
GregorianCalendar c = new GregorianCalendar();
c.setTime(date);
DatatypeFactory df;
try {
df = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
throw new RuntimeException(e);
}
XMLGregorianCalendar xcal = df.newXMLGregorianCalendar(c);
return xcal;
}
}
class ExamUnmarshallListener extends Unmarshaller.Listener {
// 一次处理一条记录,因此内存占用量低,
// 可以由于解析拥有大量记录的XML文件。
public void afterUnmarshal(Object target, Object parent) {
// 如果有类似于
// 但是若必须
// if (target instanceof HeaderType) {
// // 获取基本信息,身份验证等。
// }
if (target instanceof PersonType) {
// 我们可以在这里进行业务操作,比如Check,插入DB等
PersonType p = (PersonType) target;
System.out.println(JAXBTest.personToString(p));
if ("991110102".equals(p.getId())) {
throw new RuntimeException("示例业务异常");
}
}
}
public void beforeUnmarshal(Object target, Object parent) {
}
}
|