<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    上善若水
    In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
    posts - 146,comments - 147,trackbacks - 0

    現(xiàn)在參與的項目是一個純Application Server,整個Server都是自己搭建的,使用JMS消息實現(xiàn)客戶端和服務(wù)器的交互,交互的數(shù)據(jù)格式采用XML。說來慚愧,開始為了趕進度,所有XML消息都是使用字符串拼接的,而XML的解析則是使用DOM方式查找的。我很早就看這些代碼不爽了,可惜一直沒有時間去重構(gòu),最近項目加了幾個人,而且美國那邊也開始漸漸的把這個項目開發(fā)的控制權(quán)交給我們了,所以我開始有一些按自己的方式開發(fā)的機會了。因而最近動手開始重構(gòu)這些字符串拼接的代碼。

    XMLJava Bean的解析框架,熟悉一點的只有DigesterXStreamDigester貌似只能從XML文件解析成Java Bean對象,所以只能選擇XStream來做了,而且同組的其他項目也有在用XStream。一直聽說XStream的使用比較簡單,而且我對ThoughtWorks這家公司一直比較有好感,所以還以為引入XStream不會花太多時間,然而使用以后才發(fā)現(xiàn)XStream并沒有想象的你那么簡單。不過這個也有可能是因為我不想改變原來的XML數(shù)據(jù)格式,而之前的XML數(shù)據(jù)格式的設(shè)計自然不會考慮到如何便利的使用XStream。因而記錄在使用過程中遇到的問題,供后來人參考,也為自己以后如果打算開其源碼提供參考。廢話就到這里了,接下來步入正題。

    首先對于簡單的引用,XStream使用起來確實比較簡單,比如自定義標(biāo)簽的屬性、使用屬性和使用子標(biāo)簽的定義等:

    @XStreamAlias("request")
    public class XmlRequest1 {
        
    private static XStream xstream;
        
    static {
            xstream 
    = new XStream();
            xstream.autodetectAnnotations(
    true);
        }
       
        @XStreamAsAttribute
        
    private String from;
       
        @XStreamAsAttribute
        @XStreamAlias(
    "calculate-method")
        
    private String calculateMethod;
       
        @XStreamAlias(
    "request-time")
        private Date requestTime;
     
        @XStreamAlias(
    "input-files")
        
    private List<InputFileInfo> inputFiles;
       
        
    public static String toXml(XmlRequest1 request) {
            StringWriter writer 
    = new StringWriter();
            writer.append(Constants.XML_HEADER);
            xstream.toXML(request, writer);
            
    return writer.toString();
        }
        
    public static XmlRequest1 toInstance(String xmlContent) {
            
    return (XmlRequest1)xstream.fromXML(xmlContent);
    }

        @XStreamAlias(
    "input-file")
        
    public static class InputFileInfo {
            
    private String type;
            
    private String fileName;
            
        }
        
    public static void main(String[] args) {
            XmlRequest1 request 
    = buildXmlRequest();
            System.out.println(XmlRequest1.toXml(request));
        }
        
    private static XmlRequest1 buildXmlRequest() {
            
        }
    }

     對以上Request定義,我們可以得到如下結(jié)果:

    <?xml version="1.0" encoding="UTF-8"?>
    <request from="levin@host" calculate-method="advanced">
     
    <request-time>2012-11-28 17:11:54.664 UTC</request-time>
     
    <input-files>
        
    <input-file>
          
    <type>DATA</type>
          
    <fileName>data.2012.11.29.dat</fileName>
        
    </input-file>
        
    <input-file>
          
    <type>CALENDAR</type>
          
    <fileName>calendar.2012.11.29.dat</fileName>
        
    </input-file>
     
    </input-files>
    </request>

    可惜這個世界不會那么清凈,這個格式有些時候貌似并不符合要求,比如request-time的格式、input-files的格式,我們實際需要的格式是這樣的:

    <?xml version="1.0" encoding="UTF-8"?>
    <request from="levin@host" calculate-method="advanced">
     
    <request-time>20121128T17:51:05</request-time>
     
    <input-file type="DATA">data.2012.11.29.dat</input-file>
     
    <input-file type="CALENDAR">calendar.2012.11.29.dat</input-file>
    </request>

    對不同Date格式的支持可以是用Converter實現(xiàn),在XStream中默認(rèn)使用自己實現(xiàn)的DateConverter,它支持的格式是:yyyy-MM-dd HH:mm:ss.S 'UTC',然而我們現(xiàn)在需要的格式是yyyy-MM-dd’T’HH:mm:ss,如果使用XStream直接注冊DateConverter,可以使用配置自己的DateConverter,但是由于DateConverter的構(gòu)造函數(shù)的定義以及@XStreamConverter的構(gòu)造函數(shù)參數(shù)的支持方式的限制,貌似DateConverter不能很好的支持注解方式的注冊,因而我時間了一個自己的DateConverter以支持注解:

    public class LevinDateConverter extends DateConverter {
        
    public LevinDateConverter(String dateFormat) {
            
    super(dateFormat, new String[] { dateFormat });
        }
    }

    requestTime字段中需要加入以下注解定義:

    @XStreamConverter(value=LevinDateConverter.class, strings={"yyyyMMdd'T'HH:mm:ss"})
    @XStreamAlias(
    "request-time")
    private Date requestTime;

    對集合類,XStream提供了@XStreamImplicit注解,以將集合中的內(nèi)容攤平到上一層XML元素中,其中itemFieldName的值為其使用的標(biāo)簽名,此時InputFileInfo類中不需要@XStreamAlias標(biāo)簽的定義:

    @XStreamImplicit(itemFieldName="input-file")
    private List<InputFileInfo> inputFiles;

    InputFileInfo中的字段,type作為屬性很容易,只要為它加上@XStreamAsAttribute注解即可,而將fileName作為input-file標(biāo)簽的一個內(nèi)容字符串,則需要使用ToAttributedValueConverter,其中Converter的參數(shù)為需要作為字符串內(nèi)容的字段名:

    @XStreamConverter(value=ToAttributedValueConverter.class, strings={"fileName"})
    public static class InputFileInfo {
        @XStreamAsAttribute
        
    private String type;
    private String fileName;

    }

    XStream對枚舉類型的支持貌似不怎么好,默認(rèn)注冊的EnumSingleValueConverter只是使用了Enum提供的name()和靜態(tài)的valueOf()方法將enum轉(zhuǎn)換成String或?qū)?/span>String轉(zhuǎn)換回enum。然而有些時候XML的字符串和類定義的enum值并不完全匹配,最常見的就是大小寫的不匹配,此時需要寫自己的Converter。在這種情況下,我一般會在enum中定義一個name屬性,這樣就可以自定義enum的字符串表示。比如有TimePeriodenum

    public enum TimePeriod {
        MONTHLY(
    "monthly"), WEEKLY("weekly"), DAILY("daily");
       
        
    private String name;
       
        
    public String getName() {
            
    return name;
        }
       
        
    private TimePeriod(String name) {
            
    this.name = name;
        }
       
        
    public static TimePeriod toEnum(String timePeriod) {
            
    try {
                
    return Enum.valueOf(TimePeriod.class, timePeriod);
            } 
    catch(Exception ex) {
                
    for(TimePeriod period : TimePeriod.values()) {
                    
    if(period.getName().equalsIgnoreCase(timePeriod)) {
                        
    return period;
                    }
                }
                
    throw new IllegalArgumentException("Cannot convert <" + timePeriod + "> to TimePeriod enum");
            }
        }
    }

    我們可以編寫以下Converter以實現(xiàn)對枚舉類型的更寬的容錯性:

    public class LevinEnumSingleNameConverter extends EnumSingleValueConverter {
        
    private static final String CUSTOM_ENUM_NAME_METHOD = "getName";
        
    private static final String CUSTOM_ENUM_VALUE_OF_METHOD = "toEnum";
       
        
    private Class<? extends Enum<?>> enumType;
     
        
    public LevinEnumSingleNameConverter(Class<? extends Enum<?>> type) {
            
    super(type);
            
    this.enumType = type;
        }
     
        @Override
        
    public String toString(Object obj) {
            Method method 
    = getCustomEnumNameMethod();
            
    if(method == null) {
                
    return super.toString(obj);
            } 
    else {
                
    try {
                    
    return (String)method.invoke(obj, (Object[])null);
                } 
    catch(Exception ex) {
                    
    return super.toString(obj);
                }
            }
        }
     
        @Override
        
    public Object fromString(String str) {
            Method method 
    = getCustomEnumStaticValueOfMethod();
            
    if(method == null) {
                
    return enhancedFromString(str);
            }
            
    try {
                
    return method.invoke(null, str);
            } 
    catch(Exception ex) {
                
    return enhancedFromString(str);
            }
        }
       
        
    private Method getCustomEnumNameMethod() {
            
    try {
                
    return enumType.getMethod(CUSTOM_ENUM_NAME_METHOD, (Class<?>[])null);
            } 
    catch(Exception ex) {
                
    return null;
            }
        }
       
        
    private Method getCustomEnumStaticValueOfMethod() {
            
    try {
                Method method 
    = enumType.getMethod(CUSTOM_ENUM_VALUE_OF_METHOD, (Class<?>[])null);
                
    if(method.getModifiers() == Modifier.STATIC) {
                    
    return method;
                }
                
    return null;
            } 
    catch(Exception ex) {
                
    return null;
            }
        }
       
        
    private Object enhancedFromString(String str) {
            
    try {
                
    return super.fromString(str);
            } 
    catch(Exception ex) {
                
    for(Enum<?> item : enumType.getEnumConstants()) {
                    
    if(item.name().equalsIgnoreCase(str)) {
                        
    return item;
                    }
                }
                
    throw new IllegalStateException("Cannot converter <" + str + "> to enum <" + enumType + ">");
            }
        }
    }

    如下方式使用即可:

    @XStreamAsAttribute
    @XStreamAlias(
    "time-period")
    @XStreamConverter(value
    =LevinEnumSingleNameConverter.class)
    private TimePeriod timePeriod;

    double類型,貌似默認(rèn)的DoubleConverter實現(xiàn)依然不給力,它不支持自定義的格式,比如我們想在序列化的時候用一下格式: ###,##0.0########,此時又需要編寫自己的Converter

    public class FormatableDoubleConverter extends DoubleConverter {
        
    private String pattern;
        
    private DecimalFormat formatter;
       
        
    public FormatableDoubleConverter(String pattern) {
            
    this.pattern = pattern;
            
    this.formatter = new DecimalFormat(pattern);
        }
       
        @Override
        
    public String toString(Object obj) {
            
    if(formatter == null) {
                
    return super.toString(obj);
            } 
    else {
                
    return formatter.format(obj);
            }
        }
       
        @Override
        
    public Object fromString(String str) {
            
    try {
                
    return super.fromString(str);
            } 
    catch(Exception ex) {
                
    if(formatter != null) {
                    
    try {
                        
    return formatter.parse(str);
                    } 
    catch(Exception e) {
                        
    throw new IllegalArgumentException("Cannot parse <" + str + "> to double value", e);
                    }
                }
                
    throw new IllegalArgumentException("Cannot parse <" + str + "> to double value", ex);
            }
        }
       
        
    public String getPattern() {
            
    return pattern;
        }
    }

    使用方式和之前的Converter類似:

    @XStreamAsAttribute
    @XStreamConverter(value
    =FormatableDoubleConverter.class, strings={"###,##0.0########"})
    private double value;

    最后,還有兩個XStream沒法實現(xiàn)的,或者說我沒有找到一個更好的實現(xiàn)方式的場景。第一種場景是XStream不能很好的處理對象組合問題:

    在面向?qū)ο缶幊讨校话惚M量的傾向于抽取相同的數(shù)據(jù)成一個類,而通過組合的方式構(gòu)建整個數(shù)據(jù)結(jié)構(gòu)。比如Student類中有nameaddressAddress是一個類,它包含citycodestreet等信息,此時如果要對Student對象做如下格式序列化:

    <student name=”Levin”>
     
    <city>shanghai</city>
     
    <street>zhangjiang</street>
     
    <code>201203</code>
    </student>

    貌似我沒有找到可以實現(xiàn)的方式,XStream能做是在中間加一層address標(biāo)簽。對這種場景的解決方案,一種是將Address中的屬性平攤到Student類中,另一種是讓Student繼承自Address類。不過貌似這兩種都不是比較理想的辦法。

    第二種場景是XStream不能很好的處理多態(tài)問題:

    比如我們有一個Trade類,它可能表示不同的產(chǎn)品:

    public class Trade {
        
    private String tradeId;
        private Product product;

    }
    abstract class Product {
        
    private String name;
        
    public Product(String name) {
            
    this.name = name;
    }

    }
    class FX extends Product {
        
    private double ratio;
        
    public FX() {
            
    super("fx");
        }
        
    }
    class Future extends Product {
        
    private double maturity;
        
    public Future() {
            
    super("future");
        }
        
    }

    通過一些簡單的設(shè)置,我們能得到如下XML格式:

    <trades>
     
    <trade trade-id="001">
        
    <product class="levin.xstream.blog.FX" name="fx" ratio="0.59"/>
     
    </trade>
     
    <trade trade-id="002">
        
    <product class="levin.xstream.blog.Future" name="future" maturity="2.123"/>
     
    </trade>
    </trades>

    作為數(shù)據(jù)文件,對Java類的定義顯然是不合理的,因而簡單一些,我們可以編寫自己的Converterclass屬性從product中去除:

    xstream.registerConverter(new ProductConverter(
            xstream.getMapper(), xstream.getReflectionProvider()));
     
        
    public ProductConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
            
    super(mapper, reflectionProvider);
        }
       
        @Override
        
    public boolean canConvert(@SuppressWarnings("rawtypes") Class type) {
            
    return Product.class.isAssignableFrom(type);
        }
     
        @Override
        
    protected Object instantiateNewInstance(HierarchicalStreamReader reader, UnmarshallingContext context) {
            Object currentObject 
    = context.currentObject();
            
    if(currentObject != null) {
                
    return currentObject;
            }
           
            String name 
    = reader.getAttribute("name");
            
    if("fx".equals(name)) {
                
    return reflectionProvider.newInstance(FX.class);
            } 
    else if("future".equals(name)) {
                
    return reflectionProvider.newInstance(Future.class);
            }
            
    throw new IllegalStateException("Cannot convert <" + name + "> product");
        }
    }

    在所有Production上定義@XStreamAlias(“product”)注解。這時的XML輸出結(jié)果為:

    <trades>
     
    <trade trade-id="001">
        
    <product name="fx" ratio="0.59"/>
     
    </trade>
     
    <trade trade-id="002">
        
    <product name="future" maturity="2.123"/>
     
    </trade>
    </trades>

    然而如果有人希望XML的輸出結(jié)果如下呢?

    <trades>
     
    <trade trade-id="001">
        
    <fx ratio="0.59"/>
     
    </trade>
     
    <trade trade-id="002">
        
    <future maturity="2.123"/>
     
    </trade>
    </trades>

    大概找了一下,可能可以定義自己的Mapper來解決,不過XStream的源碼貌似比較復(fù)雜,沒有時間深究這個問題,留著以后慢慢解決吧。

    補充:

    Map類型數(shù)據(jù),XStream默認(rèn)使用以下格式顯示:

    <map class="linked-hash-map">
        
    <entry>
          
    <string>key1</string>
          
    <string>value1</string>
        
    </entry>
        
    <entry>
          
    <string>key2</string>
          
    <string>value2</string>
        
    </entry>
     
    </map>

     

    但是對一些簡單的Map,我們希望如下顯示:

     <map>
        
    <entry key="key1" value="value1"/>
        
    <entry key="key2" value="value2"/>
     
    </map>

    對這種需求需要通過編寫Converter解決,繼承自MapConverter,覆蓋以下函數(shù),這里的Map默認(rèn)keyvalue都是String類型,如果他們不是String類型,需要另外添加邏輯:

    @SuppressWarnings("rawtypes")
    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer,
            MarshallingContext context) {
        Map map 
    = (Map) source;
        
    for (Iterator iterator = map.entrySet().iterator(); iterator.hasNext();) {
            Entry entry 
    = (Entry) iterator.next();
            ExtendedHierarchicalStreamWriterHelper.startNode(writer, mapper()
                    .serializedClass(Map.Entry.
    class), entry.getClass());
     
            writer.addAttribute(
    "key", entry.getKey().toString());
            writer.addAttribute(
    "value", entry.getValue().toString());
            writer.endNode();
        }
    }
     
    @Override
    @SuppressWarnings({ 
    "unchecked""rawtypes" })
    protected void putCurrentEntryIntoMap(HierarchicalStreamReader reader,
            UnmarshallingContext context, Map map, Map target) {
        Object key 
    = reader.getAttribute("key");
        Object value 
    = reader.getAttribute("value");
     
        target.put(key, value);
    }

    但是只是使用Converter,得到的結(jié)果多了一個class屬性:

     <map class="linked-hash-map">
        
    <entry key="key1" value="value1"/>
        
    <entry key="key2" value="value2"/>
     
    </map>

    XStream中,如果定義的字段是一個父類或接口,在序列化是會默認(rèn)加入class屬性以確定反序列化時用的類,為了去掉這個class屬性,可以定義默認(rèn)的實現(xiàn)類來解決(雖然感覺這種解決方案不太好,但是目前還沒有找到更好的解決方案)。

    xstream.addDefaultImplementation(LinkedHashMap.class, Map.class);

     

    posted on 2012-11-30 01:50 DLevin 閱讀(25026) 評論(3)  編輯  收藏 所屬分類: 經(jīng)驗積累

    FeedBack:
    # re: 使用XStream序列化、反序列化XML數(shù)據(jù)時遇到的各種問題
    2013-10-24 15:39 | Sy
    厲害。最后一部份寫的,真是精彩。  回復(fù)  更多評論
      
    # re: 使用XStream序列化、反序列化XML數(shù)據(jù)時遇到的各種問題
    2015-12-18 16:35 | laughing
    樓主你好,

    不知道繼承的那個問題你有沒有解決掉?我現(xiàn)在也遇到這個問題,如果你解決掉麻煩講解一下,謝謝  回復(fù)  更多評論
      
    # re: 使用XStream序列化、反序列化XML數(shù)據(jù)時遇到的各種問題
    2016-05-19 18:50 | MR熊
    繼承上,子類對象 在toxml時, XStream只將子類的屬性轉(zhuǎn)xml了, 父類的屬性沒轉(zhuǎn)換xml為何呢?  回復(fù)  更多評論
      
    主站蜘蛛池模板: 国产免费无遮挡精品视频| 国内精品久久久久影院免费| 亚洲日韩AV一区二区三区四区| 亚洲精品熟女国产| 亚洲国产韩国一区二区| 亚洲综合精品一二三区在线| 日韩亚洲Av人人夜夜澡人人爽| 亚洲综合精品香蕉久久网97| 亚洲最大黄色网址| 亚洲精品午夜国产va久久| 中文有码亚洲制服av片| 亚洲国产精品无码久久久秋霞1| 亚洲av日韩精品久久久久久a| 理论亚洲区美一区二区三区| 美女视频黄频a免费| 无码精品人妻一区二区三区免费| 无码免费又爽又高潮喷水的视频| 国产线视频精品免费观看视频| 日韩精品无码免费专区午夜不卡| 久久久久久一品道精品免费看| 99久久国产免费-99久久国产免费| 国产高清不卡免费在线| 成人啪精品视频免费网站| 国产在线不卡免费播放| 亚洲XX00视频| 亚洲AV无码久久| 亚洲丰满熟女一区二区v| 久久精品国产亚洲AV电影网| 一道本不卡免费视频| 久久爰www免费人成| 三年片在线观看免费观看高清电影 | 俄罗斯极品美女毛片免费播放 | 最近中文字幕大全免费视频| 手机看黄av免费网址| 国产视频精品免费| 伊伊人成亚洲综合人网7777| 亚洲精品美女在线观看播放| 亚洲AV无码专区国产乱码不卡| yy一级毛片免费视频| 96免费精品视频在线观看| 日韩一级视频免费观看|