xstream 指定序列化的順序

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的這個解決方案

解決方案二:

  1. 定義一個聲明:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface XMLSequence {
    String[] value();
}
  1. 生成一個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;
        }
    }
}
  1. 把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>

結果正好!

完整代碼如下代碼
測試類參考測試代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章