转自:https://www.cnblogs.com/tang9139/p/4825610.html

http://www.cnblogs.com/wlsblog/p/7452882.html

这两东东本质上是有差别的,JAXB称为OX binding工具,XStream应该算序列化工具,但OX binding工具也会marshall和unmarshall,所以包含了序列化这一部分。序列化工具不一定需要提供binding的功能。既然都玩序列化,那就简单地比较一下它们两在序列化方面的强弱吧。

JAXB:Toplink JAXB 10133,应该是JAXB 1.1标准 (取消了schema的validation功能)

XStream:1.3.1

数据长度:

类型

长度

内容

XStraem

351

<com.oocl.frm.ws.sample.Employee>

<name>Liufei</name>

<age>40</age>

<address>

<street>Zhaojiabang</street>

<country>China</country>

<city>Shanghai</city>

<doorNum>789</doorNum>

<empName>Afka liu</empName>

</address>

<salary>20000.0</salary>

<isActive>false</isActive>

<sexy>F</sexy>

</com.oocl.frm.ws.sample.Employee>

Toplink JAXB

589(已经去掉了white space)

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

<ns0:employee xsi:schemaLocation="http://www.oocl.com/frm/ws/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns0="http://www.oocl.com/frm/ws/jaxb" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><ns0:name>Liufei</ns0:name><ns0:age>40</ns0:age><ns0:salary>20000.0</ns0:salary><ns0:sexy>F</ns0:sexy><ns0:isActive>false</ns0:isActive><ns0:address><ns0:street>Zhaojiabang</ns0:street><ns0:country>China</ns0:country><ns0:city>Shanghai</ns0:city><ns0:doorNum>789</ns0:doorNum><ns0:empName>Afka liu</ns0:empName></ns0:address></ns0:employee>

时间:序列化和反序列化1000000次。

类型

序列化(ms)

反序列化(ms)

XStraem

90148

135878

Toplink JAXB

34872

56557

结果对比:数据量XStream占优势,时间性能上Toplink Jaxb占明显优势

总结(只从序列化功能角度看)

JAXB: 优点

  • J2EE标准
  • 运行时间比XStream少

缺点

  • 用起来不方便:需要把手动的把business object转换成schema object,当然也可以直接将schema object作为business object,或者采用反射的方法。
  • 有一定的局限性:需要schema或者annotation
  • 数据量稍大

XStream优点:

  • 用起来方便
  • 不需要schema,拿过来就转
  • 数据量稍小

缺点:

  • 非标准
  • 时间性能差
  • ------------------------------
  • JavaArchitecture for XML Binding (JAXB) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到XML实例文档。从另一方面来讲,JAXB提供了快速而简便的方法将XML模式绑定到Java表示,从而使得Java开发者在Java应用程序中能方便地结合XML数据和处理函数。

    这意味着你不需要处理甚至不需要知道XML编程技巧就能在Java应用程序中利用平台核心XML数据的灵活性。而且,可以充分利用XML的优势而不用依赖于复杂的XML处理模型如SAX或DOM。JAXB 隐藏了细节并且取消了SAX和DOM中没用的关系——生成的JAXB类仅描述原始模型中定义的关系。其结果是结合了高度可移植Java代码和高度可移植的XML数据。其中这些代码可用来创建灵活、轻便的应用程序和Web服务。     JAXB(javaArchitecture for XML Binding) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术。该过程中,JAXB也提供了将XML实例文档反向生成Java对象树的方法,并能将Java对象树的内容重新写到 XML实例文档。  Jaxb 2.0是JDK 1.6的组成部分。我们不需要下载第三方jar包 即可做到轻松转换。Jaxb2使用了JDK的新特性,如:Annotation、GenericType等,需要在即将转换的JavaBean中添加annotation注解。

    一、重要概念

    • JAXBContext类,是应用的入口,用于管理XML/Java绑定信息。
    • Marshaller接口,将Java对象序列化为XML数据。
    • Unmarshaller接口,将XML数据反序列化为Java对象。
    • @XmlType,将Java类或枚举类型映射到XML模式类型
    • @XmlAccessorType(XmlAccessType.FIELD) ,控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标 注)字段到XML。其他值还有XmlAccessType.PROPERTY和XmlAccessType.NONE。
    • @XmlAccessorOrder,控制JAXB 绑定类中属性和字段的排序。
    • @XmlJavaTypeAdapter,使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML。
    • @XmlElementWrapper ,对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。
    • @XmlRootElement,将Java类或枚举类型映射到XML元素。
    • @XmlElement,将Java类的一个属性映射到与属性同名的一个XML元素。
    • @XmlAttribute,将Java类的一个属性映射到与属性同名的一个XML属性。

    二、工具类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    package com.ehaoyao.jobcenter.order.webservices.jztdzy.Util;
     
    import org.apache.poi.ss.formula.functions.T;
     
    import javax.xml.bind.*;
    import javax.xml.namespace.QName;
    import java.io.Reader;
    import java.io.StringReader;
    import java.io.StringWriter;
     
    public class JAXBUtil {
     
        /**
         * beanToXml
         * @param obj
         * @param encoding
         * @return
         */
        public static String toXml(Object obj,String encoding) {
            try {
                StringWriter sw = new StringWriter();
                JAXBContext context = JAXBCache.instance().getJAXBContext(obj.getClass());
                Marshaller marshaller = context.createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
                marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);// 是否省略xm头声明信息
                marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
                marshaller.marshal(obj, sw);
                return sw.toString().replace("ns2:","").replace(":ns2","");
            catch(JAXBException e) {
                e.printStackTrace();
                return e.getMessage();
            }
        }
     
     
        /**
         * xmlToBean
         * @param xml
         * @param c
         * @param <T>
         * @return
         */
        @SuppressWarnings("unchecked")
        public static <T> T toBean(String xml, Class<T> c,String encoding) {
            T t = null;
            try {
                JAXBContext context = JAXBContext.newInstance(c);
                Unmarshaller unmarshaller = context.createUnmarshaller();
                t = (T) unmarshaller.unmarshal(new StringReader(xml));
            catch (Exception e) {
                e.printStackTrace();
            }
            return t;
        }
    }

      

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    package com.ehaoyao.jobcenter.order.webservices.jztdzy.Util;
     
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    public final class JAXBCache {
        private static final JAXBCache instance = new JAXBCache();
        private final ConcurrentMap<String, JAXBContext> contextCache = new ConcurrentHashMap<String, JAXBContext>();
        private JAXBCache() {
        }
        public static JAXBCache instance() {
            return instance;
        }
        JAXBContext getJAXBContext(Class<?> clazz) throws JAXBException {
            JAXBContext context = contextCache.get(clazz.getName());
            if ( context == null )
            {
                context = JAXBContext.newInstance(clazz);
                contextCache.putIfAbsent(clazz.getName(), context);
            }
            return context;
        }
    }

      

    1
    2
    3
    4
    5
    6
    package com.ehaoyao.jobcenter.order.webservices.jztdzy.Util;
     
    public class XMLNameSpace {
        public static final String ADMIN_PREFIX = "";
        public static final String ADMIN_URI = "http://WebServices.WS.JointownTech.com//";
    }

      

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    package com.ehaoyao.jobcenter.order.webservices.jztdzy.Util;
     
    import com.ehaoyao.jobcenter.order.webservices.jztdzy.model.ElecPrescription;
    import com.thoughtworks.xstream.annotations.XStreamAlias;
     
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlType;
     
    /**
     * Created by wls on 2017/8/15.
     */
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(propOrder= {"result","errorDesc","elecPrescription"})
    public class AddPrescriptionToMes2ItemResponse {
    //    @XStreamAlias("result")
        @XmlElement(name="result",namespace = XMLNameSpace.ADMIN_URI)
        private String result;
        @XmlElement(required = false)
        private String errorDesc;
        @XmlElement(required = false)
        private ElecPrescription elecPrescription;
     
        public String getResult() {
            return result;
        }
     
        public void setResult(String result) {
            this.result = result;
        }
     
        public String getErrorDesc() {
            return errorDesc;
        }
     
        public void setErrorDesc(String errorDesc) {
            this.errorDesc = errorDesc;
        }
     
        public ElecPrescription getElecPrescription() {
            return elecPrescription;
        }
     
        public void setElecPrescription(ElecPrescription elecPrescription) {
            this.elecPrescription = elecPrescription;
        }
     
        @Override
        public String toString() {
            return "AddPrescriptionToMes2ItemResponse{" +
                "result='" + result + '\'' +
                ", errorDesc='" + errorDesc + '\'' +
                ", elecPrescription=" + elecPrescription +
                '}';
        }
    }

      

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    package com.ehaoyao.jobcenter.order.webservices.jztdzy.Util;
     
    import com.thoughtworks.xstream.annotations.XStreamAlias;
     
    import javax.xml.bind.annotation.*;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
     
    /**
     * Created by wls on 2017/8/15.
     */
    //@XStreamAlias("string")
    @XmlRootElement(name = "string",namespace = XMLNameSpace.ADMIN_URI)
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(propOrder= {"response"})
    public class AddPrescriptionToMes2Response {
        @XmlElement(name = "response",namespace = XMLNameSpace.ADMIN_URI)
        private AddPrescriptionToMes2ItemResponse response;
     
        public AddPrescriptionToMes2ItemResponse getResponse() {
            return response;
        }
     
        public void setResponse(AddPrescriptionToMes2ItemResponse response) {
            this.response = response;
        }
     
        @Override
        public String toString() {
            return "AddPrescriptionToMes2Response{" +
                "response=" + response +
                '}';
        }
    }

      

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package com.ehaoyao.jobcenter.order.webservices.jztdzy.Util;
     
    import com.ehaoyao.jobcenter.order.webservices.jztdzy.model.ElecPrescription;
     
    import javax.xml.bind.annotation.XmlAccessType;
    import javax.xml.bind.annotation.XmlAccessorType;
    import javax.xml.bind.annotation.XmlElement;
    import javax.xml.bind.annotation.XmlType;
     
    /**
     * Created by wls on 2017/8/17.
     */
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(propOrder= {"result","errorDesc"},namespace = XMLNameSpace.ADMIN_URI)
    public class SetWXPrescriptionInfoItemResponse {
        @XmlElement(name = "result",namespace = XMLNameSpace.ADMIN_URI)
        private ElecPrescription result;
        private String errorDesc;
     
     
        public ElecPrescription getResult() {
            return result;
        }
     
        public void setResult(ElecPrescription result) {
            this.result = result;
        }
     
        public String getErrorDesc() {
            return errorDesc;
        }
     
        public void setErrorDesc(String errorDesc) {
            this.errorDesc = errorDesc;
        }
     
    }

      

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package com.ehaoyao.jobcenter.order.webservices.jztdzy.Util;
     
    import com.thoughtworks.xstream.annotations.XStreamAlias;
     
    import javax.xml.bind.annotation.*;
     
    /**
     * Created by wls on 2017/8/17.
     */
    //@XStreamAlias("string")
    @XmlRootElement(name = "string",namespace = XMLNameSpace.ADMIN_URI)
    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(propOrder= {"response"})
    public class SetWXPrescriptionInfoResponse {
        @XmlElement(name = "response",namespace = XMLNameSpace.ADMIN_URI)
        private SetWXPrescriptionInfoItemResponse response;
     
        public SetWXPrescriptionInfoItemResponse getResponse() {
            return response;
        }
     
        public void setResponse(SetWXPrescriptionInfoItemResponse response) {
            this.response = response;
        }
    }

      三、测试类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    package com.ehaoyao.jobcenter.test;
     
    import com.alibaba.fastjson.JSONObject;
    import com.ehaoyao.jobcenter.order.webservices.jztdzy.Util.AddPrescriptionToMes2Response;
    import com.ehaoyao.jobcenter.order.webservices.jztdzy.Util.JAXBUtil;
    import com.ehaoyao.jobcenter.order.webservices.jztdzy.Util.SetWXPrescriptionInfoResponse;
    import com.ehaoyao.jobcenter.order.webservices.jztdzy.model.ElecPrescription;
    import org.apache.poi.ss.formula.functions.T;
     
    import static org.junit.Assert.*;
     
    public class JaxbUtilTest {
     
        public static void main(String[] args) {
            ElecPrescription elecPrescription = new ElecPrescription();
            elecPrescription.setAddress("测试地址");
            elecPrescription.setAge(12);
            elecPrescription.setAgentNum(1);
            elecPrescription.setBillDates("2017-08-19 12:02:21");
            elecPrescription.setBlank2("1");
            elecPrescription.setBlank3("1");
            elecPrescription.setBusinessId("1435");
            String ret = JAXBUtil.toXml(elecPrescription,"UTF-8");
            System.out.println(ret);
            elecPrescription = (ElecPrescription) JAXBUtil.toBean(ret,ElecPrescription.class,"UTF-8");
            System.out.println(elecPrescription);
     
            ret = "<string xmlns=\"http://WebServices.WS.JointownTech.com//\"><response><result>1</result></response></string>";
            AddPrescriptionToMes2Response addPrescriptionToMes2Response = JAXBUtil.toBean(ret,AddPrescriptionToMes2Response.class,"UTF-8");
            System.out.println(addPrescriptionToMes2Response!=null? JSONObject.toJSONString(addPrescriptionToMes2Response):null);
     
            ret = "<string xmlns=\"http://WebServices.WS.JointownTech.com//\"><response><result><BillDates>2017-08-16</BillDates><OutPrescriptionCode>100004</OutPrescriptionCode><OutPrescriptionSn>1004</OutPrescriptionSn><tpyq>代煎</tpyq><zgyq></zgyq><Patient>张三</Patient><Gender>女</Gender><Age>12</Age><CFDates>2017-08-17</CFDates><IsPregnancy>否</IsPregnancy><cflx>外用</cflx><AgentNum>1</AgentNum><Remark></Remark><Opinion></Opinion><BusinessName>好药师三方渠道</BusinessName><orgname>湖北公司</orgname><Dosages>1</Dosages><jyyq>1</jyyq><jyfa>微压(密闭)解表(15min)</jyfa><PatientCode></PatientCode><PictureIP>http://10.2.72.200:131/DCIM/010100003639731150</PictureIP><GoodsDatas><GoodsData><GoodsName>炒蒺藜</GoodsName><Dosage>1</Dosage><GoodsTpyq>无</GoodsTpyq><InvoiceP>0</InvoiceP></GoodsData></GoodsDatas><LogisticsCost>0</LogisticsCost><ProcessingCost>0</ProcessingCost><cfzt>作废</cfzt><cfjd><CFREVIEWSTAFF></CFREVIEWSTAFF><CFREVIEWTIME></CFREVIEWTIME><MIXSTAFF></MIXSTAFF><MIXREQUESTTIME></MIXREQUESTTIME><MIXSURETIME></MIXSURETIME><MIXREVIEWSTAFF></MIXREVIEWSTAFF><MIXREVIEWTIME></MIXREVIEWTIME><SOAKSTAFF></SOAKSTAFF><SOAKREQUESTTIME></SOAKREQUESTTIME><SOAKSURETIME></SOAKSURETIME><TISANESTAFF></TISANESTAFF><TISANEREQUESTTIME></TISANEREQUESTTIME><TISANESURETIME></TISANESURETIME>  <OINTMENTSTAFF></OINTMENTSTAFF><OINTMENTREQUESTTIME></OINTMENTREQUESTTIME><OINTMENTSURETIME></OINTMENTSURETIME><INNERPACKINGSTAFF></INNERPACKINGSTAFF><INNERPACKINGTIME></INNERPACKINGTIME><OUTERPACKINGSTAFF>zyadmin</OUTERPACKINGSTAFF><OUTERPACKINGTIME>2017-08-16 16:41:56</OUTERPACKINGTIME></cfjd><psjd><LogisticsCompany></LogisticsCompany><ExpressNumber></ExpressNumber></psjd></result></response></string>";
            SetWXPrescriptionInfoResponse setWXPrescriptionInfoResponse = JAXBUtil.toBean(ret,SetWXPrescriptionInfoResponse.class,"UTF-8");
            System.out.println(setWXPrescriptionInfoResponse!=null? JSONObject.toJSONString(setWXPrescriptionInfoResponse):null);
        }
    }
    }

      

      四、运行效果

      

JAXB和XStream比较的更多相关文章

  1. spring oxm入门(包含demo)

    O/X Mapper 是什么? Spring 3.0 的一个新特性是 O/X Mapper.O/X 映射器这个概念并不新鲜,O 代表 Object,X 代表 XML.它的目的是在 Java 对象(几乎 ...

  2. Spring技术内幕:设计理念和整体架构概述(转)

    程序员都很崇拜技术大神,很大一部分是因为他们发现和解决问题的能力,特别是线上出现紧急问题时,总是能够快速定位和解决. 一方面,他们有深厚的技术基础,对应用的技术知其所以然,另一方面,在采坑的过程中不断 ...

  3. XStream、JAXB 日期(Date)、数字(Number)格式化输出xml

    XStream.Jaxb是java中用于对象xml序列化/反序列化 的经典开源项目,利用它们将对象转换成xml时,经常会遇到日期(Date).数字按指定格式输出的需求,下面是使用示例: 一.日期字段格 ...

  4. RCE via XStream object deserialization && SECURITY-247 / CVE-2016-0792 XML reconstruction Object Code Inject

    catalogue . Java xStream . DynamicProxyConverter . java.beans.EventHandler . RCE via XStream object ...

  5. java生成解析xml的另外两种方法Xstream

    Xstream生成和解析xm和JAXB生成和解析xml的方法. 一,Xstream Xstream非jdk自带的,需要到入Xstream-1.4.3.jar和xpp3_min-1.1.4.jar 1. ...

  6. JAXB注解的使用详解

    前言: 最近一直在做各种接口的对接,接触最多的数据类型就是JSON和XML数据,还有XML中包含JSON的数据,而在Java中对象和XML之间的转换经常用到JAXB注解,抽空在这里总结一下,首先做一下 ...

  7. Springboot中使用Xstream进行XML与Bean 相互转换

    在现今的项目开发中,虽然数据的传输大部分都是用json格式来进行传输,但是xml毕竟也会有一些老的项目在进行使用,正常的老式方法是通过获取节点来进行一系列操作,个人感觉太过于复杂.繁琐.推荐一套简单的 ...

  8. Java操作XML的工具:JAXB

    JavaArchitecture for XML Binding (JAXB) 是一个业界的标准,是一项可以根据XML Schema产生Java类的技术.该过程中,JAXB也提供了将XML实例文档反向 ...

  9. XStream将java对象转换为xml时,对象字段中的下划线“_”,转换后变成了两个的解决办法

            在前几天的一个项目中,由于数据库字段的命名原因 其中有两项:一项叫做"市场价格"一项叫做"商店价格" 为了便于区分,遂分别将其命名为market ...

随机推荐

  1. 汉诺塔IV---hdu2077

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2077 #include <stdio.h> #include <stdlib.h&g ...

  2. 借助 Django 的 smart_str 和 smart_unicode 进行编码转换(转)

    原文:http://www.dirk.sh/diary/using-django-smart_str-smart_unicode/ Django 为字符编码的转换提供了非常简洁的方法: 1.djang ...

  3. python web框架 django 练习1 django 1.11版本

    django练习 在我自己项目里创建一个xiaoliu的文件夹 里面创建s1.py 文件 s1.py文件 里面写各种函数 from django.shortcuts import HttpRespon ...

  4. MySQL协议分析(2)

    MySQL协议分析(2) 此阶段是在压缩传输无加密条件下进行的协议分析 思路 结合Oracle官网的说明和自己用wireshark加python进行数据包分析 步骤 客户端与服务器端是否压缩的协商阶段 ...

  5. for…else和while…else

    当while语句配备else子句时,如果while子句内嵌的循环体在整个循环过程中没有执行break语句(循环体中没有break语句,或者循环体中有break语句但是始终未执行),那么循环过程结束后, ...

  6. ActiveRecord多数据库配置

    ActiveRecord 的多数据库配置基本沿袭了 NHibernate 的思想,只不过在配置文件结构上作了些调整.NHibernate的配置也是基于配置得来的,配置多个SessionFactory传 ...

  7. SpringCloud Config Server中{application}等占位符使用场景设置默认拉去分支

    Spring Cloud Config服务器支持一个Git仓库URL,其中包含{application}和{profile}(以及{label})的占位符. 1.各个占位符所代表的含义 applica ...

  8. 235. Lowest Common Ancestor of a Binary Search Tree(LCA最低公共祖先)

      Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the ...

  9. hihocoder1479 三等分

    地址:http://hihocoder.com/problemset/problem/1479 题目: 三等分 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi最近 ...

  10. 开源一款android 偷拍 app【静拍】豌豆荚、flyme商店已经上线

    首先先花3秒时间,预览下下app的大概是做啥的,解决啥痛点的:) app: 本地下载地址 需求点: 1:音量键可以拍照 2:没有快门声.闪光灯 3:锁屏下.或者是在其他程序界面都可以拍照 思路: 1: ...