用Castor 處理XML文檔

fmms 12年前發布 | 296K 次閱讀 Castor XML操作類庫

用Castor 處理XML文檔
——Castor可以完成Java和XML的相互轉換

前面有介紹過json-lib這個框架,在線博文:http://www.cnblogs.com/hoojo/archive/2011/04/21/2023805.html

以及Jackson這個框架,在線博文:http://www.cnblogs.com/hoojo/archive/2011/04/22/2024628.html

它們都可以完成Java對象到XML的轉換,但是還不是那么的完善。還有XStream對JSON及XML的支持,它可以對JSON或XML的完美轉換。在線博文:

http://www.cnblogs.com/hoojo/archive/2011/04/22/2025197.html

這里將介紹Castor來完成Java對象到xml的相互轉換。它是怎么樣轉換的?和前面不同的是castor可以用一個mapping.xml文件來描述轉換后的Java對象的xml基本形態,類似于xStream的annotation,這點還是非常不錯的。下面我們就來看看Castor是怎么樣完成 Java對象到XML之間的相互轉換吧。

一、 準備工作

1、 官方資源

本示例會運用到如下依賴包(jar包):

 

資源及jar包下載:http://www.castor.org/download.html

junit jar下載地址:

https://github.com/KentBeck/junit/downloads

關于官方提供的mapping配置相關示例、文檔:

http://www.castor.org/xml-mapping.html

ibm提供的castor方面的文檔資料:

http://www.google.com.hk/search?hl=zh-CN&newwindow=1&safe=strict&client=aff-cs-360se&hs=Gon&biw=1349&bih=603&q=castor+site%3Awww.ibm.com%2Fdeveloperworks%2Fcn%2Fxml%2F&aq=f&aqi=&aql=&oq=

2、 程序測試運行代碼

package com.hoo.test;

import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.exolab.castor.mapping.Mapping; import org.exolab.castor.mapping.MappingException; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.Marshaller; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.ValidationException; import org.junit.After; import org.junit.Before; import org.junit.Test; import com.hoo.entity.Account; import com.hoo.entity.AccountArray; import com.hoo.entity.Birthday; import com.hoo.entity.ListBean; import com.hoo.entity.MapBean;

/**

  • <b>function:</b>Castor完成Java對象到XML的相互轉換
  • 依賴jar: castor-1.3.jar
  • castor-1.3-core.jar
  • junit-4.8.2.jar
  • log4j-1.2.16.jar
  • commons-logging.jar
  • @author hoojo
  • @createDate 2011-4-21 下午07:57:26
  • @file CastorTest.java
  • @package com.hoo.test
  • @project WebHttpUtils
  • @blog http://blog.csdn.net/IBM_hoojo
  • @email hoojo_@126.com
  • @version 1.0 */ public class CastorTest {

    private Account bean = null; private Mapping mapping = new Mapping(); private StringWriter writer = null; private StringReader reader = null;

    @Before public void init() {

     bean = new Account();
     bean.setAddress("北京");
     bean.setEmail("email");
     bean.setId(1);
     bean.setName("jack");
     Birthday day = new Birthday();
     day.setBirthday("2010-11-22");
     bean.setBirthday(day);
    
     try {
         /**
          * 加載mapping.xml,此文件是對需要轉換的Java對象的配置描述,
          * 即:轉換后的Java對象的xml內容的轉換規則
          */
         mapping.loadMapping(System.getProperty("user.dir") + "\\src\\mapping.xml");
     } catch (IOException e) {
         e.printStackTrace();
     } catch (MappingException e) {
         e.printStackTrace();
     }
    

    }

    @After public void destory() {

     bean = null;
     mapping = null;
     try {
         if (writer != null) {
             writer.flush();
             writer.close();
         }
         if (reader != null) {
             reader.close();
         }
     } catch (IOException e) {
         e.printStackTrace();
     }
     System.gc();
    

    }

    public void fail(Object o) {

     System.out.println(o);
    

    }

    public void failRed(Object o) {

     System.err.println(o);
    

    } }</pre>

    Mapping對象可以完成Java對象到XML的編組和解組,它需要先設定一個mapping.xml,通過xml對JavaObject的描述。來完成JavaObject的編組、解組工作。

    3、 看看即將被轉換的JavaEntity代碼

    Account

    package com.hoo.entity;

public class Account { private int id; private String name; private String email; private String address; private Birthday birthday;

//setter、getter
@Override
public String toString() {
    return this.id + "#" + this.name + "#" + this.email + "#" + this.address + "#" + this.birthday;
}

}

Birthday

package com.hoo.entity;

public class Birthday { private String birthday;

public Birthday(String birthday) {
    super();
    this.birthday = birthday;
}
//getter、setter
public Birthday() {}

@Override
public String toString() {
    return this.birthday;
}

}

AccountArray

package com.hoo.entity;

public class AccountArray { private Account[] accounts; private int size; public int getSize() { size = accounts.length; return size; } public void setSize(int size) { this.size = size; } public Account[] getAccounts() { return accounts; } public void setAccounts(Account[] accounts) { this.accounts = accounts; } }

ListBean

package com.hoo.entity;

import java.util.List;

public class ListBean { private String name; private List list; //setter、getter }

MapBean

package com.hoo.entity;

import java.util.Map;

public class MapBean { private Map<String, Object> map;

public Map<String, Object> getMap() {
    return map;
}
public void setMap(Map<String, Object> map) {
    this.map = map;
}

}</pre>

二、 編組JavaObject到XML

1、 將JavaBean編組,轉換成XML

/**

  • <b>function:</b>將Javabean編組,轉換成XML
  • @author hoojo
  • @createDate 2011-4-22 下午12:08:48 */ @Test public void writeBean2XML() { writer = new StringWriter(); try {
     //編組
     Marshaller.marshal(bean, writer);
     fail(writer);
    
    } catch (MarshalException e) {
     e.printStackTrace();
    
    } catch (Exception e) {
     e.printStackTrace();
    
    } }</pre>

    代碼很簡單,通過Marshaller的marshal方法來完成Java對象到XML的編組(序列化、轉換)工作。

    運行后的結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <account id="1"><address>北京</address><email>email</email><name>jack</name>
    <birthday><birthday>2010-11-22</birthday></birthday></account>

    2、 將List集合轉換成XML

    /**
  • <b>function:</b>將List轉換成xml
  • @author hoojo
  • @createDate 2011-4-22 下午12:11:00 */ @Test public void writeList2XML() { writer = new StringWriter(); List<Account> list = new ArrayList<Account>(); list.add(bean); bean = new Account(); bean.setName("tom"); bean.setId(223); list.add(bean); try {
     Marshaller.marshal(list, writer);
     fail(writer);
    
    } catch (MarshalException e) {
     e.printStackTrace();
    
    } catch (Exception e) {
     e.printStackTrace();
    
    } }</pre>
    運行后,結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <array-list>
    <account xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" id="1" xsi:type="java:com.hoo.entity.Account">
    <address>北京</address><email>email< /email><name>jack</name><birthday><birthday>2010-11-22</birthday></birthday></account>
    <account xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:java="http://java.sun.com" id="223" xsi:type="java:com.hoo.entity.Account">
    <name>tom</name>
    </account>
    </array-list>


    怎么樣,List存放的是2個Account吧。

    3、 將Array數組轉換成XML

    /**
  • <b>function:</b>將Array數組轉換成XML
  • @author hoojo
  • @createDate 2011-4-22 下午12:11:25 */ @Test public void writeArray2XML() { writer = new StringWriter(); Account[] acc = new Account[2]; acc[0] = bean; bean = new Account(); bean.setName("tom"); bean.setId(223); acc[1] = bean;

    try {

     Marshaller.marshal(acc, writer);
     fail(writer);
    

    } catch (MarshalException e) {

     e.printStackTrace();
    

    } catch (Exception e) {

     e.printStackTrace();
    

    } }</pre>

    結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <array><account id="1"><address>北京</address><email>email</email& gt;<name>jack</name>
    <birthday><birthday>2010-11-22</birthday></birthday></account>
    <account id="223"><name>tom</name></account></array>


    4、 轉換其他Java類型

    /**

  • <b>function:</b>將Java常用類型編組成xml
  • @author hoojo
  • @createDate 2011-4-22 下午12:11:44 */ @Test public void writeObject2XML() { writer = new StringWriter(); try {
     Marshaller.marshal(true, writer);
     Marshaller.marshal(9527, writer);
     Marshaller.marshal(2.2f, writer);
     Marshaller.marshal(1.11d, writer);
     Marshaller.marshal("lucy", writer);
     Marshaller.marshal("hello castor".getBytes(), writer);
     Marshaller.marshal(new char[] { 'a', 'b', 'c' }, writer);
     Marshaller.marshal(new String[] { "hi", "spring", "castor" }, writer);
     fail(writer);
    
    } catch (MarshalException e) {
     e.printStackTrace();
    
    } catch (Exception e) {
     e.printStackTrace();
    
    } }</pre>

    結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <boolean>true</boolean><?xml version="1.0" encoding="UTF-8"?>
    <integer>9527</integer><?xml version="1.0" encoding="UTF-8"?>
    <float>2.2</float><?xml version="1.0" encoding="UTF-8"?>
    <double>1.11</double><?xml version="1.0" encoding="UTF-8"?>
    <string>lucy</string><?xml version="1.0" encoding="UTF-8"?>
    <[-b>aGVsbG8gY2FzdG9y</[-b><?xml version="1.0" encoding="UTF-8"?>
    <array><character>a</character><character>b</character><character>c</character></array><?xml version="1.0" encoding="UTF-8"?>
    <array><string>hi</string><string>spring</string><string>castor</string></array>

    都是類型為節點名稱,值為text。但是這里并沒有出現Map,如果轉換Map需要mapping進行配置。下面再慢慢道來-.-

    5、 將xml解組成JavaBean

    /**
  • <b>function:</b>將XML內容,解組成JavaBean
  • @author hoojo
  • @createDate 2011-4-22 下午12:12:14 */ @Test public void readXML2Bean() {

    String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +

                 "<account id=\"1\"><address>北京</address>" +
                 "<name>jack</name><email>email</email>" +
                 "<birthday><birthday>2010-11-22</birthday></birthday></account>";
    

    reader = new StringReader(xml);

    try {

     //解組
     Account account = (Account) Unmarshaller.unmarshal(Account.class, reader);
     fail(account);
    

    } catch (MarshalException e) {

     e.printStackTrace();
    

    } catch (ValidationException e) {

     e.printStackTrace();
    

    } }</pre>

    結果如下:

    1#jack#email#北京#2010-11-22

    其他的類型,如:map、list、array都不能成功解組。因為這些類型里面有很多系統默認的xml描述。但是利用mapping和自定義JavaBean就可以成功編組和解組了。下面看看mapping是怎么玩轉這些類型的。

    三、 利用mapping配置,編組JavaObject、解組XML

    最開始的init方法就提供了mapping,讓我們對mapping這個配置有了大概的了解。下面我們將詳細介紹mapping是個什么:

    1、 在此之前我們設置過mapping.xml。如果不設置,肯定是不能轉換成我們想要的XML的。那么,mapping.xml配置文件是怎么配置Account這個對象的呢?

    mapping.xml配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapping PUBLIC "-//EXOLAB/Castor Mapping DTD Version 1.0//EN" "http://castor.org/mapping.dtd"&gt;
    <mapping>
     <class name="com.hoo.entity.Account" auto-complete="true">

     <map-to xml="Account"/>
    
     <field name="id" type="integer">
         <bind-xml name="id" node="attribute" />
     </field>
    
     <field name="name" type="string">
         <bind-xml name="name" node="element" />
     </field>
    
     <field name="email" type="string">
         <bind-xml name="email" node="element" />
     </field>
    
     <field name="address" type="string">
         <bind-xml name="address" node="element" />
     </field>
    
     <field name="birthday" type="com.hoo.entity.Birthday">
         <bind-xml name="生日" node="element" />
     </field>
    

    </class>

    <class name="com.hoo.entity.Birthday">

     <map-to xml="birthday" />
    
     <field name="birthday" type="string">
         <bind-xml name="birthday" node="attribute" />
     </field>
    

    </class> </mapping></pre>

    首先,看看這個xml文檔的根元素是mapping,在mapping中可以配置class。也就是我們要轉換的JavaObject的配置描述了。

    class元素的name屬性就是配置的JavaObject的classpath路徑了。

    關于class元素的auto-complate屬性,如果這個屬性的值為ture。那么編組后的xml,castor會自動給沒有在mapping配置文件進行配置的屬性自動編組(轉換)到xml中。如果為false,那么在mapping配置文件中出現的屬性將在編組后不現在在編組后的xml中。

    map-to就是當前class編組后的xml文檔的節點元素名稱。

    field就是描述JavaObject中的屬性,name是Java對象的屬性名稱,type是類型。關于配置的type類型也有規定,你可以參考:http://www.castor.org/xml-mapping.html的field配置講解。

    而field還有其他的屬性配置,如get-method應該是getter方法、set-method應該是setter的方法、has-mehtod 應該是hashCode方法,有時候我們不一定要提高getter、setter方法,我們需要用自己的方法名稱來代替setter、getter。如果當前field配置的是集合類型,那么你需要給field元素配置collection屬性。

    bind-xml就是綁定(編組)成xml后的xml內容的描述,name就是編組后xml的節點元素名稱,node有2個值,分別是 attribute、element。attribute是屬性,它會在節點元素的屬性中顯示,例如:<account id=”2”></account>

    而element則是單獨的一個元素,例如:<account><id>2</id></account>

    就這個樣子的。

    mapping.xml還可以有其他標簽,如:

    <include href="other_mapping_file.xml"/>

    導入外部xml文件,可以分多個配置。

    好了,先將這么多的mapping方面的內容。我們還是看看實際運行的示例吧,代碼如下:

    /
      <b>function:</b>將XML內容解組成Java對象
     
    @author hoojo
      @createDate 2011-4-22 下午12:13:28
     
    /
    @Test
    public void bean4Mapping2XML() {
        writer = new StringWriter();
        try {
            //編組
            Marshaller mar = new Marshaller(writer);
            mar.setMapping(mapping);
            mar.marshal(bean);
            fail(writer);
           
            //解組
            reader = new StringReader(writer.toString());
            Unmarshaller unmar = new Unmarshaller(Account.class);
            unmar.setMapping(mapping);
           
            Account account = (Account) unmar.unmarshal(reader);
            fail(account);
        } catch (MarshalException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    運行后結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <Account id="1"><name>jack</name><email>email</email>& lt;address>北京</address><生日 birthday="2010-11-22"/></Account>
    1#jack#email#北京#2010-11-22

    上面的xml的根節點是Account,這個功勞就來源于mapping配置中的map-to元素,而根節點的id屬性是有field和bind-xml 來完成的。當bind-xml的node值為attribute時,就會以屬性的方式顯示。當node為element時,就會像后面name、 email一樣,以元素名稱顯示。

    再看看上面的mapping文件中的Account的配置,有個auto-complate屬性,如果把這個屬性的值設置成false,會怎么樣?那我們趕緊試試。

    沒有發現上面異樣,但是當我們刪除下面配置的filed的時候,就發現有變化了。

    結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <Account><name>jack</name><email>email< /email><address>北京</address><生日 birthday="2010-11-22"/></Account>
    0#jack#email#北京#2010-11-22

    發現id沒有顯示在xml中,那么我們再將auto-complate的屬性設置true,會有什么驚喜?

    結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <Account id="1"><name>jack</name><email>email</email>& lt;address>北京</address><生日 birthday="2010-11-22"/></Account>
    1#jack#email#北京#2010-11-22

    發現id又回來了,但是Account的配置中并沒有配置id的field。這是為什么,其實auto-comlate在上面已經講過了。Castor在編組時會自動將int類型的屬性,顯示在父元素的屬性中。并且JavaObject中有的屬性沒有在mapping配置文件中配置,castor也會自動將其編組在xml中。

    下面我們看看map-to配置的用法,map-to的主要屬性是name,也就是我們把當前根元素重命名的名稱。Map-to還有2個屬性可以用,分別是 ns-uri、ns-prefix。看名稱就知道它大概的意識,一個是命名空間的uri另一個則是命名空間的前綴。我們給上面mapping加上這兩個屬性看看。

    <map-to xml="Account" ns-uri="http://hoojo.cnblogs.com&quot; ns-prefix="castor"/>

    結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <castor:Account xmlns:castor="http://hoojo.cnblogs.com&quot; id="1"><castor:name>jack</castor:name><castor:email>email</castor:email>
    <castor:address>北京</castor:address><castor:生日 birthday="2010-11-22"/></castor:Account>
    1#jack#email#北京#2010-11-22

    發現了什么?節點元素都帶上了ns-prefix的值,而根元素則有了xml的ns。

    2、 將一段XML格式字符串轉換成JavaBean

    @Test
    public void readBean4Mapping2XML() {
        String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                    "<Account id=\"2241\"><name>jack</name><email>email< /email><address>北京</address><生日 birthday=\"2010-11-22\"/></Account>";
        try {
            reader = new StringReader(xml);
            Unmarshaller unmar = new Unmarshaller(Account.class);
            unmar.setMapping(mapping);
           
            Account account = (Account) unmar.unmarshal(reader);
            fail(account);
        } catch (MarshalException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    運行后結果如下:

    2241#jack#email#北京#2010-11-22

    3、 將XML內容解組成Java的Array

    /

      <b>function:</b>將XML內容解組成Java的Array
     
    @author hoojo
      @createDate 2011-4-22 下午12:14:50
     
    /
    @Test
    public void array4Mapping2XML() {
        writer = new StringWriter();
        Account[] acc = new Account[2];
        acc[0] = bean;
        bean = new Account();
        bean.setName("tom");
        bean.setId(223);
        acc[1] = bean;
        AccountArray array = new AccountArray();
        array.setAccounts(acc);
        try {
            Marshaller mar = new Marshaller(writer);
            mar.setMapping(mapping);
            mar.marshal(array);
            fail(writer);
           
            reader = new StringReader(writer.toString());
            Unmarshaller unmar = new Unmarshaller(AccountArray.class);
            unmar.setMapping(mapping);
            array = (AccountArray) unmar.unmarshal(reader);
            fail(array.getSize());
            fail(array.getAccounts()[0]);
            fail(array.getAccounts()[1]);
        } catch (MarshalException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    AccountArray的mapping配置如下:

    <class name="com.hoo.entity.AccountArray">
        <map-to xml="account-array"/>
        <field name="size" type="int" />
        <field name="accounts" collection="array" type="com.hoo.entity.Account">
            <bind-xml name="accounts" auto-naming="deriveByClass"/>
        </field>
    </class>

    collection表示是數組,auto-maming有2中值,一種是類driverByClass,另一種則是driverByField是屬性。

    運行后,結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <account-array><size>2</size><Account id="1"><name>jack</name><email>email</email>& lt;address>北京</address>
    <生日 birthday="2010-11-22"/></Account><Account id="223"><name>tom</name></Account></account-array>
    2
    1#jack#email#北京#2010-11-22
    223#tom#null#null#null

    4、 將Map編組、解組成JavaObject

    /
      <b>function:</b>xml轉換成Java的Map
     
    @author hoojo
      @createDate 2011-4-22 下午12:15:18
     
    /
    @Test
    public void map4Mapping2XML() {
        writer = new StringWriter();
        MapBean mapBean = new MapBean();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("No1", bean);
        bean = new Account();
        bean.setName("tom");
        bean.setId(223);
        map.put("No2", bean);
        mapBean.setMap(map);
       
        try {
            Marshaller mar = new Marshaller(writer);
            mar.setMapping(mapping);
            mar.marshal(mapBean);
            fail(writer);
           
            reader = new StringReader(writer.toString());
            Unmarshaller unmar = new Unmarshaller(MapBean.class);
            unmar.setMapping(mapping);
            mapBean = (MapBean) unmar.unmarshal(reader);
            fail(mapBean.getMap());
        } catch (MarshalException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    Mapping配置

    <class name="com.hoo.entity.MapBean">
        <field name="map" collection="map">
            <bind-xml name="map">
                <class name="org.exolab.castor.mapping.MapItem">
                    <field name="key" type="java.lang.String">
                        <bind-xml name="key" node="attribute" />
                    </field>
                    <field name="value" type="com.hoo.entity.Account">
                        <bind-xml name="value" auto-naming="deriveByClass"/>
                    </field>
                </class>
            </bind-xml>
        </field>
    </class>

    上面的map配置必須這樣配置,利用org.exolab.castor.mapping.MapItem這個class,完成key、value的配置。

    結果如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <map-bean><map key="No2"><Account id="223"><name>tom</name></Account></map>
    <map key="No1"><Account id="1"><name>jack</name><email>email</email>& lt;address>北京</address>
    <生日 birthday="2010-11-22"/></Account></map></map-bean>
    {No2=223#tom#null#null#null, No1=1#jack#email#北京#2010-11-22}

    5、 JavaList編組、解組XML

    /

      <b>function:</b>List到XML的相互轉換
     
    @author hoojo
      @createDate 2011-4-22 下午12:16:04
     
    /
    @SuppressWarnings("unchecked")
    @Test
    public void listForMapping2XML() {
        writer = new StringWriter();
       
        List<Account> list = new ArrayList<Account>();
        list.add(bean);
        bean = new Account();
        bean.setName("tom");
        bean.setId(223);
        list.add(bean);
       
        ListBean listBean = new ListBean();
        listBean.setList(list);
        try {
            Marshaller mar = new Marshaller(writer);
            mar.setMapping(mapping);
            mar.marshal(listBean);
            fail(writer);
           
            reader = new StringReader(writer.toString());
            Unmarshaller unmar = new Unmarshaller(ListBean.class);
            unmar.setMapping(mapping);
            listBean = (ListBean) unmar.unmarshal(reader);
            fail(listBean.getList().size());
            for (Account acc : (List<Account>)listBean.getList()) {
                fail(acc);
            }
        } catch (MarshalException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    Mapping配置

    <class name="com.hoo.entity.ListBean">
        <map-to xml="listBean"/>
        <field name="list" collection="arraylist" type="com.hoo.entity.Account">
            <bind-xml name="beans" auto-naming="deriveByClass"/>
        </field>
    </class>

    結果:

    <?xml version="1.0" encoding="UTF-8"?>
    <listBean><Account id="1"><name>jack</name><email>email</email>& lt;address>北京</address>
    <生日 birthday="2010-11-22"/></Account><Account id="223"><name>tom</name></Account></listBean>
    2
    1#jack#email#北京#2010-11-22
    223#tom#null#null#null

    原文地址: http://www.cnblogs.com/hoojo/archive/2011/04/25/2026819.html

 本文由用戶 fmms 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!