xstream 指定序列化的順序
需求:在使用xstream轉化Javabean->xml的過程中,有時候,我們需要指定xml化的順序,xstream默認的是按字符定義的順序來xml化的,
但是,有的時候如果我們使用的繼承某些class,而無法改變的時候,這就非常難辦了。
解決方案一:
官方給了一個類:通過 com.thoughtworks.xstream.converters.reflection.FieldDictionary.來實現指定xml化的順序字典,
可以參考例子地址:
public void testSortsFieldOrderWithArray() {
SortableFieldKeySorter sorter = new SortableFieldKeySorter();
sorter.registerFieldOrder(MommyBear.class,
new String[]{"absde", "absese", "accsed", "money"});
XStream xstream = new XStream(new PureJavaReflectionProvider(new FieldDictionary(sorter)));
xstream.alias("mommy", MommyBear.class);
MommyBear root = new MommyBear();
root.absde = "ccc";
root.absese = "bbb";
root.accsed = "aaa";
// root.money = "12";
System.out.println(xstream.toXML(root));
}
public static class MommyBear implements Serializable {
String absde;
String absese;
String accsed;
String money;
}
然而通過這個方式有缺點,那就是如果有N個屬性成員,如果有沒有指定的話,那麼會報錯。而且不具備通用性,如果有M個類需要指定的話,那麼
工作量也是非常大的。
爲了解決這個問題,我們可以參考Stack Overflow的這個解決方案
解決方案二:
- 定義一個聲明:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface XMLSequence {
String[] value();
}
- 生成一個FieldKeySorter的實現類,用來注入xstream實例中
public class SequenceFieldKeySorter implements FieldKeySorter {
@Override
public Map sort(final Class type, final Map keyedByFieldKey) {
Annotation sequence = type.getAnnotation(XMLSequence.class);
if (sequence != null) {
final String[] fieldsOrder = ((XMLSequence) sequence).value();
Map result = new LinkedHashMap();
Set<Map.Entry<FieldKey, Field>> fields = keyedByFieldKey.entrySet();
for (String fieldName : fieldsOrder) {
if (fieldName != null) {
for (Map.Entry<FieldKey, Field> fieldEntry : fields) {
if
(fieldName.equals(fieldEntry.getKey().getFieldName())) {
result.put(fieldEntry.getKey(),
fieldEntry.getValue());
}
}
}
}
return result;
} else {
return keyedByFieldKey;
}
}
}
- 把fieldSorter 的實現類注入到xStream中,代碼如下:
XStream xStream = new XStream(new PureJavaReflectionProvider(new FieldDictionary(new PartialSeqFieldKeySorter())));
在方案而中,如果有了XmlSequence聲明,如下:
@Setter
@Getter
@XMLSequence({"imID", "zipCode"})
public class Buyer {
private String email;
private String zipCode;
private String address;
private String imID;
private String name;
private String phone;
private String mobile;
}
如果你聲明中只定義了N-x個的話,那麼,剩下的都不會被xml化,結果如下(address沒有被xml化):
<buyer>
<imID>imId001</imID>
<zipCode>00001</zipCode>
</buyer>
這是個問題,我們需要聲明所有的屬性,對於多屬性成員來說,這是不可取的。爲了
解決這個問題,我們重新實現SequenceFieldKeySorter中的 public Map sort(Class type, Map keyedByFieldKey) 方法,代碼如下:
@Override
public Map sort(Class type, Map keyedByFieldKey) {
Annotation sequence = type.getAnnotation(XMLSequence.class);
if (sequence != null) {
final String[] fieldsOrder = ((XMLSequence) sequence).value();
Map<FieldKey, Field> custom = new LinkedHashMap<>();
Map<FieldKey, Field> notCustom = new LinkedHashMap<>();
Set<Map.Entry<FieldKey, Field>> fields = keyedByFieldKey.entrySet();
for (String fieldName : fieldsOrder) {
if (fieldName != null) {
for (Map.Entry<FieldKey, Field> fieldEntry : fields) {
if (fieldName.equals(fieldEntry.getKey().getFieldName())) {
custom.put(fieldEntry.getKey(), fieldEntry.getValue());
} else {
notCustom.put(fieldEntry.getKey(), fieldEntry.getValue());
}
}
}
}
custom.putAll(notCustom);
return custom;
} else {
return keyedByFieldKey;
}
}
兩者的區別在於這裏我們實現瞭如果沒有聲明的,我們也加進返回結果中了
for (Map.Entry<FieldKey, Field> fieldEntry : fields) {
if (fieldName.equals(fieldEntry.getKey().getFieldName())) {
custom.put(fieldEntry.getKey(), fieldEntry.getValue());
} else {
notCustom.put(fieldEntry.getKey(), fieldEntry.getValue());
}
}
//沒有定義的添加到結果中
custom.putAll(notCustom);
return custom;
重新定義一個類:
public class PartialSeqFieldKeySorter implements FieldKeySorter {
@Override
public Map sort(Class type, Map keyedByFieldKey) {
Annotation sequence = type.getAnnotation(XMLSequence.class);
if (sequence != null) {
final String[] fieldsOrder = ((XMLSequence) sequence).value();
Map<FieldKey, Field> custom = new LinkedHashMap<>();
Map<FieldKey, Field> notCustom = new LinkedHashMap<>();
Set<Map.Entry<FieldKey, Field>> fields = keyedByFieldKey.entrySet();
for (String fieldName : fieldsOrder) {
if (fieldName != null) {
for (Map.Entry<FieldKey, Field> fieldEntry : fields) {
if (fieldName.equals(fieldEntry.getKey().getFieldName())) {
custom.put(fieldEntry.getKey(), fieldEntry.getValue());
} else {
notCustom.put(fieldEntry.getKey(), fieldEntry.getValue());
}
}
}
}
custom.putAll(notCustom);
return custom;
} else {
return keyedByFieldKey;
}
}
}
這樣我們就可以只定義自己需要的屬性成員就可以了,不改變上面Buyer.class的聲明,這裏我們可以生成所有的不爲空的成員
<buyer>
<imID>imId001</imID>
<zipCode>00001</zipCode>
<address>sh.cn</address>
</buyer>
結果正好!