天天看點

JavaBean 和 XML 互相轉換(四)

作者:JU幫

上一篇文章:JavaBean 和 XML 互相轉換(三)

當我們 JavaBean 屬性值中存在特殊字元時,添加到 XML 中就會出現問題,有兩種方式可以處理:

  • 一種方式是将特殊字元轉義
  • 另外一種是使用 <![CDATA[]]> 包裹

對于 Hutool 提供的 JAXBUtil 預設就是使用第一種方式,今天我們就介紹第二種方式的實作,需要如下步驟:

  • 需要添加如下依賴:
<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>org.eclipse.persistence.moxy</artifactId>
    <version>2.5.2-M1</version>
</dependency>           
  • 在需要使用 <![CDATA[]]> 的 JavaBean 屬性上添加 @XmlCDATA 注解
  • 需要在 resource 中建立和需要轉義的 JavaBean 同包的目錄,并添加 jaxb.properties 檔案,内容如下:
# 用于配置建立 JAXBContext 的工廠類,該工廠類支援 BeanToXml 添加 CDATA,必須與待轉換的 Bean 同包
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory           
jaxb.properties 所在的目錄需要 JAXBUtil.beanToXml() 工具入參的類所在的包一緻,為什麼這樣,我們文章最後通過源碼解釋。

測試需要使用的類:

  • 學生類:
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class Student {

    /**
     * 學号
     */
    @XmlCDATA
    private String no;

    /**
     * 學生姓名
     */
    @XmlCDATA
    private String name;

    /**
     * 年齡
     */
    private Integer age;

    /**
     * 家庭位址
     */
    private String address;
}           
  • 測試類:
public class XmlBeanMapperTest {
    @Test
    public void testCdata() {
        Student student = new Student("1001", "<李梅>", 8, "<北京市昌平區>");
        Console.log(JAXBUtil.beanToXml(student));
    }
}           

運作結果如下:

<?xml version="1.0" encoding="UTF-8"?>
<student>
   <no><![CDATA[1001]]></no>
   <name><![CDATA[<李梅>]]></name>
   <age>8</age>
   <address><北京市昌平區></address>
</student>           
通過上面的示例可以看到,如果标注 @XmlCDATA 注解的屬性會使用 <![CDATA[]]> 包裹,特殊字元也不會被轉義,沒有标注的如果有特殊字元就會被轉義。

下面解釋為什麼 jaxb.properties 所在的目錄需要 JAXBUtil.beanToXml() 工具入參的類所在的包一緻?

我們想看 JAXBUtil.beanToXml() 的源碼,如下:

public static String beanToXml(Object bean) {
		return beanToXml(bean, CharsetUtil.CHARSET_UTF_8, true);
	}           

該方法調用了其另外一個重載方法,如下:

public static String beanToXml(Object bean, Charset charset, boolean format) {
		StringWriter writer;
		try {
			JAXBContext context = JAXBContext.newInstance(bean.getClass());
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, format);
			marshaller.setProperty(Marshaller.JAXB_ENCODING, charset.name());
			writer = new StringWriter();
			marshaller.marshal(bean, writer);
		} catch (Exception e) {
			throw new UtilException("convertToXml 錯誤:" + e.getMessage(), e);
		}
		return writer.toString();
	}           

該方法中通過 JAXBContext.newInstance() 建構了一個 JAXBContext 執行個體,我們點進 newInstance() 方法看一下:

public static JAXBContext newInstance( Class... classesToBeBound )
        throws JAXBException {

        return newInstance(classesToBeBound,Collections.<String,Object>emptyMap());
    }           

該方法調用了另一個重載方法,如下:

public static JAXBContext newInstance( Class[] classesToBeBound, Map<String,?> properties )
        throws JAXBException {
        // 省略
        return ContextFinder.find(classesToBeBound,properties);
    }           

該方法通過 ContextFinder.find(classesToBeBound,properties) 建立 JAXBContext,點進 find 方法,如下:

static JAXBContext find( Class[] classes, Map properties ) throws JAXBException {
				// 省略其他代碼
        for (final Class c : classes) {
            ClassLoader classLoader = getClassClassLoader(c);
          	// c 是需要轉換為 XML 的 JavaBean 類
            Package pkg = c.getPackage(); // 擷取該類的包資訊
            if(pkg==null)
                continue; 
            String packageName = pkg.getName().replace('.', '/'); // 将包中的 . 替換為 /
          
          	// 将替換為 / 的包名拼接 jaxb.properties,組成的相對路徑,後續通過該路徑查找檔案,
          	// 是以需要在 resource 下建立 JavaBean 包相同的目錄儲存 jaxb.properties
            String resourceName = packageName+"/jaxb.properties"; 
         // 省略其他代碼
        return newInstance(classes, properties, PLATFORM_DEFAULT_FACTORY_CLASS);
    }           

如果文章對大家有所幫助,歡迎點贊、關注、評論。

JavaBean 和 XML 互相轉換(四)