XSD-8 使用 XJC 生成的代碼
8 使用 XJC 生成的代碼
每次在創建 JAXBContext 實例時,JAXBContext 內部都需要維護好 Java 類和 XML 之間的映射關係,這個操作十分消耗性能
。不過JAXBContext是線程安全的,可以共享。一種較好的做法是,在程序初始化時,傳入所有的 Class
,在使用時直接調用創建好的 JAXBContext 實例,而不是在每次使用時創建。
被緩存的 JAXBContext,爲了性能上的考慮,將會對 JAXBContext 做緩存,不過緩存使用到了WeakReference,不用擔心 GC 問題。
初識化 JAXBContext
在多線程環境下,應該使用類似下面的方式來初識化 JAXBContext。
/**
* a single ton object that is responsible for converting XML to a <Sample> object and <Sample> to an XML.
*/
public class SampleXmlSerializer {
// the singleton instance
private volatile static SampleXmlSerializer instance;
// marshaller and unmarshaller
private final Marshaller marshaller; // java to xml
private final Unmarshaller unmarshaller; // xml to java
private final Unmarshaller unmarshallerH; // xml to java with validator
// validation event collector for xsd
// If the XML data validation fails, an UnmarshalException (from javax.xml.bind) is thrown.
// create your own error messages, you can pass a ValidationEventCollector to the unmarshaller which will store validation events into it so that you can retrieve an event and query its individual attributes.
private final ValidationEventCollector validationEventCollector;
// Object factory
private final ObjectFactory factory = new ObjectFactory();
// xsd schema file path
private final String xsdPath = "src/main/resources/config/employee.xsd";
private SampleXmlSerializer() throws JAXBException {
// create the JAXBContext object only here, to prevent memory leak
JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
unmarshaller = jc.createUnmarshaller();
unmarshallerH = loadUnmarshallerH(xsdPath);
validationEventCollector = new ValidationEventCollector();
}
}
其中,
- ObjectFactory 指由 xjc 生成的 ObjectFactory.java 類;
- Sample class 是指 XSD 文檔中
<element>
對應 Java Bean Class;
單實例解析器
創建單實例靜態函數,使用緩存的 JAXBContext 實例。
/**
* @return the singleton's instance (create it if necessary)
* @throws JAXBException if an error occurred while initializing the object
*/
public static SampleXmlSerializer getInstance() throws JAXBException {
if (instance == null) {
synchronized (SampleXmlSerializer.class) {
// double check the reference
if (instance == null) {
instance = new SampleXmlSerializer();
}
}
}
return instance;
}
序列化與反序列化
JAXBContext 是線程安全的,但是 Marshaller, Unmarshaller 都不是線程安全的。在多線程環境下,應該使用類似下面的 synchronized 同步關鍵字來序列化對象樹和反序列化 XML 文檔。
序列化對象樹 serialize 函數
/**
* serializes a request object to an XML string
*
* @param request callback request
* @return the given request serialized to an XML string
* @throws JAXBException if an error occurs during marshaling
*/
public String serialize(Sample request) throws JAXBException {
// wrap the object in a JAXB element to serialize it
JAXBElement<Sample> element = factory.createSample(request);
// output string
StringWriter writer = new StringWriter();
// marshal the request
synchronized (marshaller) {
marshaller.marshal(element, writer);
}
return writer.toString();
}
反序列化 XML 文檔 deserialize 函數
/**
* deserializes a request object from a given XML string
*
* @param xmlString XML input string
* @return callback request object that was deserialized from the input string
* @throws JAXBException if an error occurs during unmarshalling
* @throws ClassCastException if the deserialized object is not an instance of <Sample>
*/
public Sample deserialize(String xmlString) throws JAXBException {
StringReader reader = new StringReader(xmlString);
JAXBElement<Sample> element;
synchronized (unmarshaller) {
element = (JAXBElement<Sample>) unmarshaller.unmarshal(reader);
}
return element.getValue();
}
在反序列化前驗證 XML 文檔
爲什麼要驗證 XML 文檔
前文提要,XSD 可以使用標籤 <simpleType>
對基礎數據類型(String, Integer, Date, …)進行限定,例如:
定義了帶有一個限定且名爲 “password” 的元素。其值必須精確到 8 個字符:
<xs:element name="password">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:length value="8"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
這些限定信息在 xjc 工具進行 xsd 轉換 java 時,無法被添加到 JAXB 註解(Annotation),生成的 Java 代碼類似如下:
@XmlElement(name = "password")
private String password;
前文提及:
即使文檔的形式良好,仍然不能保證它們不會包含錯誤,
並且這些錯誤可能會產生嚴重的後果。
請考慮下面的情況:
您訂購的了 5 打激光打印機,而不是 5 臺。
通過 XML Schema,大部分這樣的錯誤會被您的驗證軟件捕獲到。
在數據流的某些關鍵節點/接口
,不僅需要 XML 的序列化與反序列化功能,更需要驗證請求數據是否遵循限定,給後續的數據流/業務
提供受信任
的數據源。
初始化帶驗證器(validator)的解析實例
使用 ValidationEventCollector 根據 XSD 驗證 XML 文檔,參閱:JAXB - Validate Document before It is Unmarshalled
/**
* @return Unmarshaller instance with xsd schema
*/
private Unmarshaller loadUnmarshallerH(String xsdPath) {
Schema mySchema;
// create this schema object by setting up a schema factory for the schema language of your choice.
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
String filePath = xsdPath;
File file = new File(filePath);
Unmarshaller u = null;
try {
// create the Schema object by calling the factory's method newSchema
// throw SAXException if fail
mySchema = sf.newSchema(file);
JAXBContext jc = JAXBContext.newInstance(ObjectFactory.class);
u = jc.createUnmarshaller();
// After the Unmarshaller object has been established, you pass it the schema.
u.setSchema(mySchema);
// create your own error messages
u.setEventHandler(validationEventCollector);
} catch (SAXException saxe) {
// ...(error handling)
saxe.printStackTrace();
} catch (JAXBException e) {
e.printStackTrace();
}
return u;
}
驗證器報表(validator report)
編寫 deserializeH 函數,用帶有 XSD 限定驗證器,解析符合 xsd 結構的 XML 文檔;
/**
* validate & deserializes a request object from a given XML string
*
* @param xmlString XML input string
* @return callback request object that was deserialized from the input string
* @throws JAXBException if an error occurs during unmarshalling
* @throws ClassCastException if the deserialized object is not an instance of <Sample>
*/
public Sample deserializeH(String xmlString) throws JAXBException {
// no unmarshaller available
if (unmarshallerH == null) return null;
StringReader reader = new StringReader(xmlString);
JAXBElement<Sample> element;
synchronized (unmarshallerH) {
try {
element = (JAXBElement<Sample>) unmarshallerH.unmarshal(reader);
} finally {
if (validationEventCollector != null && validationEventCollector.hasEvents()) {
// XML Schema (xsd) validate report
for (ValidationEvent ve : validationEventCollector.getEvents()) {
String msg = ve.getMessage();
ValidationEventLocator vel = ve.getLocator();
int line = vel.getLineNumber();
int column = vel.getColumnNumber();
System.err.println(xmlString + ": " + line + "." + column + ": " + msg);
}
}
}
}
return element.getValue();
}
}
上一章:XSD-7 使用 XSD 實現與 XML 的交互
目錄:學習 JAXB
下一章:XSD-9 Maven + XSD