Elim的博客

用来记录一些原创性的总结


Elim

JAXB之XmlAnyAttribute

考虑这样一种场景,你有一个元素上面会有很多不同的属性,这些属性是动态的,你不能完全预知到其中会包含哪些属性,或者属性太多了你不能一一把它们列举出来与对象的属性一一对应。这样才情景我们就可以通过一个Map来接收这些动态的属性,然后在上面使用@XmlAnyAttribute,这样在转换为对象时JAXB就会把那些不能精确匹配到对应的属性上的XML元素属性映射到对应的Map上。假设有类似下面这样一段XML需要应用动态的属性匹配。

<holder prop1="value1" prop2="value2" prop3="value3"/>

可以建立类似下面这样的代码进行匹配,需要注意的是Map对应的Key需要是javax.xml.namespace.QName类型的。

@XmlRootElement(name="holder")
@XmlAccessorType(XmlAccessType.FIELD)
class AnyAttributeHolder {
	
	@XmlAnyAttribute
	private Map<QName, String> attrs;

	public Map<QName, String> getAttrs() {
		return attrs;
	}

	public void setAttrs(Map<QName, String> attrs) {
		this.attrs = attrs;
	}
}

可以进行验证如下:

@Test
public void test2() {
	String xml = "<holder prop1=\"value1\" prop2=\"value2\" prop3=\"value3\"/>";
	AnyAttributeHolder holder = JAXB.unmarshal(new StringReader(xml), AnyAttributeHolder.class);
	
	Assert.assertTrue(holder.getAttrs().get(new QName("prop1")).equals("value1"));
	Assert.assertTrue(holder.getAttrs().get(new QName("prop2")).equals("value2"));
	Assert.assertTrue(holder.getAttrs().get(new QName("prop3")).equals("value3"));
	
}

如果你还有把它marshal为XML的需要,也是可以进行的,如:

@Test
public void test() {
	Map<QName, String> attrs = new HashMap<>();
	for (int i=0; i<5; i++) {
		attrs.put(new QName("prop" + (i+1)), "value" + (i+1));
	}
	AnyAttributeHolder holder = new AnyAttributeHolder();
	holder.setAttrs(attrs);
	
	StringWriter writer = new StringWriter();
	JAXB.marshal(holder, writer);
	System.out.println(writer);
	
}

生成的XML是如下这样:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<holder prop2="value2" prop1="value1" prop5="value5" prop4="value4" prop3="value3"/>

(注:本文由Elim写于2017年9月11日)