基于注解生成xml


大家好,我是llp。最近在对接第三方接口时,发现他们都是xml形式的报文,而且各有差异有的集合的地方包含父标签而有的又没有,于是就想着自己做一个基于注解反射去生成xml的方案。今天正好完成了,做个记录顺着做个分享,程序肯定有很多不足的地方,非常欢迎大家提出建议。

1.XMl语法

在此之前我们需要先知道一个XML文件分为如下几部分内容

1.文档声明

2.元素

3.属性

4.注释

5.CDATA区、特殊字符

具体细节,可以参考官网文档

2.定义自己的一套注解

这里我将注解分为下面几类:

1.@XmlBeanElement  用于修饰类
2.@XmlPropertyELement 用于修饰普通类型的字段
3.@XmlBeanElement 用于修饰bean类型的字段
4.@XmlCDATA 用于修饰生成CDATA区的字段
5.@XmlGatherElement 用于修饰集合类型的字段 List<String>、List<Person>、List<Map>
6.@XmlMapElement 用于修饰Map类型的字段 Map<String,String>

这里Map只考虑了普通类型的key,val方式

1.@XmlBeanElement

/**
 * xml跟标签注解(xml元素)
 * ElementType.TYPE 修饰类
 * @Retention(RetentionPolicy.RUNTIME) 运行时生效
 */

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlRootElement {

    /**
     * 别名
     */

    String name() default "";

    /**
     * 命名空间
     */

    String[] namespace() default "";

    /**
     * 版本号 用于文档声明
     */

    String version() default "v1.0";

    /**
     * 编码 用于文档声明
     */

    String encoding() default "utf-8";

}

2.@XmlPropertyELement

/**
 * xml属性注解
 * @Target(ElementType.FIELD) 修饰字段
 */

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlPropertyELement {

    /**
     * 别名
     */

    String name() default "";

    /**
     * 命名空间
     */

    String namespace() default "";

    /**
     * 是否解析空值
     */

    boolean analysisEmpty() default false;

}

3.@XmlBeanElement

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlBeanElement {

    /**
     * 别名
     */

    String name() default "";

    /**
     * 命名空间
     */

    String namespace() default "";

    /**
     * 是否解析空值
     * @return
     */

    boolean analysisEmpty() default false;

}

4.@XmlCDATA

/**
 * CDATA区
 *  <![CDATA[
 * 这里可以把你输入的字符原样显示,不会解析 xml
 *   ]]>
 * 1.因为这里设计@XmlCDATA是配合@XmlPropertyELement注解进行使用
 * 是否解析空值、命名空间、别名等交由@XmlPropertyELement控制
 * 2.@XmlCDATA注解只关注于xml中CDATA区的处理
 */

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlCDATA {
    /**
     *      <code>
     *         <!--如果希望把某些字符串,当做普通文本,使用CDATA包括 -->
     *         <![CDATA[
     *             <script data-compress=strip>
     *             function h(obj){
     *             obj.style.behavior='url(#default#homepage)';
     *             var a = obj.setHomePage('//www.baidu.com/');
     *             }
     *         </script>
     *         ]]>
     *      </code>
     */


}

5.@XmlGatherElement

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlGatherElement {

    /**
     * 别名
     */

    String name() default "";

    /**
     * 命名空间
     */

    String namespace() default "";

    /**
     * 子标签
     */

    String childName() default "";

    /**
     * 是否解析空值
     */

    boolean analysisEmpty() default false;

    /**
     * 是否显示父标签名
     */

    boolean showName() default true;

    /**
     * 是否是bean
     */

    boolean isBean() default false;
}

6.@XmlMapElement

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XmlMapElement {

    /**
     * 标签别名
     */

    String name() default "";

    /**
     * 命名空间
     */

    String namespace() default "";

    /**
     * 是否解析空值
     */

    boolean analysisEmpty() default false;
}

3.生成XML的工具类

public class XmlUtil {

    /**
     * 生成xml字符串
     *
     * @param obj xml元素注解修饰的对象
     * @return
     */

    public static String generateXml(Object obj) {
        //1.获取到class对象的
        Class<?> clazz = obj.getClass();
        //2.获取xml元素注解
        XmlRootElement xmlRootElement = clazz.getAnnotation(XmlRootElement.class);
        //3.定义一个StringBuffer对象用于拼接xml字符串
        StringBuffer xml = new StringBuffer();
        //4.文档声明 组装 <?xml version="1.0" encoding="UTF-8"?>
        xml.append("<?xml ")
                .append("version=" + xmlRootElement.version())
                .append(" ")
                .append("encoding=" + xmlRootElement.encoding())
                .append("?>");

        //5.xml元素(跟标签)拼接
        String name = xmlRootElement.name();
        //如果别名为空,以类名作为标签
        if ("".equals(name) || name == null) {
            name = clazz.getSimpleName();
        }
        xml.append("<")
                .append(name)
                .append(" ");

        String[] namespace = xmlRootElement.namespace();
        //6.遍历命名空间,拼接xml元素命名空间
        for (int i = 0; i < namespace.length; i++) {
            if (StringUtils.isNotBlank(namespace[i])) {
                xml.append(namespace[i])
                        .append(" ");
            }

        }
        //移除末尾多余的空格
        xml.deleteCharAt(xml.lastIndexOf(" "));
        xml.append(">");

        //7.拼接xml属性
        element(xml, clazz, obj);

        //8.xml元素结尾
        xml.append("</")
                .append(xmlRootElement.name())
                .append(">");
        return xml.toString();
    }

    /**
     * 组装xml属性标签内容
     *
     * @param xml
     * @param clazz
     * @param obj
     */

    private static void element(StringBuffer xml, Class<?> clazz, Object obj) {
        //1.获取类的所有字段
        Field[] fields = clazz.getDeclaredFields();
        //2.遍历所有字段
        for (Field field : fields) {
            Object val = getFieldVal(field, obj);
            //3.判断每个字段被什么类型的注解修饰
            if (field.isAnnotationPresent(XmlPropertyELement.class)) {
                //基本类型字段内容处理
                propertyElement(field, xml, val);
            } else if (field.isAnnotationPresent(XmlBeanElement.class)) {
                //对象字段内容处理
                beanELement(field, xml, val);
            } else if (field.isAnnotationPresent(XmlGatherElement.class)) {
                //集合字段内容处理
                gatherELement(field, xml, val);
            } else if (field.isAnnotationPresent(XmlMapElement.class)) {
                //map字段内容处理
                mapELement(field, xml, val);
            }
        }
    }

    /**
     * map字段内容处理
     *
     * @param field
     * @param xml
     * @param val
     */

    private static void mapELement(Field field, StringBuffer xml, Object val) {
        //1.获取@XmlMapElement注解
        XmlMapElement xmlMapElement = field.getAnnotation(XmlMapElement.class);
        //2.判断是否解析空值
        if (!xmlMapElement.analysisEmpty() && isEmpty(val)) {
            return;
        }
        //3.获取别名,如果别名为空则以字段名作为标签
        String name = xmlMapElement.name();
        if ("".equals(name) || name == null) {
            name = field.getName();
        }
        xml.append("<").append(name);
        //4.获取命名空间,如果存在则拼接
        if (!"".equals(xmlMapElement.namespace()) && xmlMapElement.namespace() != null) {
            xml.append(" ").append(xmlMapElement.namespace());
        }
        xml.append(">");
        //遍历map拼接
        if (val instanceof Map) {
            mapToXmlString(xml, (Map) val);
        } else {
            throw new RuntimeException("@XmlMapElement标识字段必须为Map类型");
        }
        //结束标签拼接
        xml.append("</").append(name).append(">");
    }

    /**
     * 集合字段内容处理
     *
     * @param field
     * @param xml
     * @param val
     */

    private static void gatherELement(Field field, StringBuffer xml, Object val) {
        //1.获取@XmlGatherElement注解
        XmlGatherElement gatherElement = field.getAnnotation(XmlGatherElement.class);
        //判断是否处理空数据
        if (!gatherElement.analysisEmpty() && isEmpty(val)) {
            return;
        }
        String name = gatherElement.name();
        //别名为空按名字装配
        if (gatherElement == null || name.length() == 0) {
            name = field.getName();
        }
        //判断是否组装父标签
        boolean showName = gatherElement.showName();
        if (showName) {
            xml.append("<").append(name);
            if (gatherElement.namespace() != null && !"".equals(gatherElement.namespace()))
                xml.append(" ").append(gatherElement.namespace());
            xml.append(">");
        }
        //获取子标签名称
        String childName = gatherElement.childName();
        //子标签为空则已父标签作为子标签
        if (isEmpty(childName))
            childName = name;
        if (val instanceof Array) {
            //处理数组数据
            //判断子标签是否需要解析
            if (!gatherElement.isBean()) {
                //不为bean对象则直接遍历数组,对每个元素进行拼接
                for (Array a : (Array[]) val) {
                    xml.append("<").append(childName).append(">").append(a).append("</").append(childName).append(">");
                }
            } else {
                //每个元素都是bean对象的数组
                for (Object ob : (Array[]) val) {
                    //组装子标签数据
                    beanOrMapEment(childName, xml, ob);
                }
            }
        } else if (val instanceof Collection) {
            //处理集合数据
            Collection list = (Collection) val;
            //判断集合子标签是否需要解析
            if (!gatherElement.isBean()) {
                for (Object ob : list) {
                    xml.append("<").append(childName).append(">").append(ob).append("</").append(childName).append(">");
                }
            } else {
                for (Object ob : list) {
                    //解析子标签
                    beanOrMapEment(childName, xml, ob);
                }
            }
        }
        //如果显示父标签,拼接父标签结束标签
        if (showName) {
            xml.append("</").append(name).append(">");
        }
    }

    /**
     * 每个元素为bean或map数据拼接
     *
     * @param childName
     * @param xml
     * @param ob
     */

    private static void beanOrMapEment(String childName, StringBuffer xml, Object ob) {
        xml.append("<").append(childName).append(">");
        //当为空时不处理
        if (ob != null) {
            //判断集合内元素是否为map
            if (ob instanceof Map) {
                //解析map数据
                mapToXmlString(xml, (Map) ob);
            } else {
                //解析bean,获取元素的class对象
                Class clazz = ob.getClass();
                //组装元素xml报文
                element(xml, clazz, ob);
            }
        }
        xml.append("</").append(childName).append(">");
    }

    /**
     * map遍历组装
     *
     * @param xml
     * @param map
     */

    private static void mapToXmlString(StringBuffer xml, Map map) {
        if (map == null || map.size() == 0) {
            return;
        }
        for (Object key : map.keySet()) {
            xml.append("<").append(key).append(">").append(map.get(key)).append("</").append(key).append(">");
        }
    }

    /**
     * bean属性字段内容处理
     *
     * @param field
     * @param xml
     * @param val
     */

    private static void beanELement(Field field, StringBuffer xml, Object val) {
        //1.获取@XmlBeanElement注解
        XmlBeanElement xmlBeanElement = field.getAnnotation(XmlBeanElement.class);
        //2.判断是否对空值进行解析
        if (!xmlBeanElement.analysisEmpty() && isEmpty(val)) {
            //如果不对空值解析且属性值为空则直接return
            return;
        }
        //3.拼接bean对象起始标签
        String name = xmlBeanElement.name();
        //如果别名为空则已字段名进行拼接
        if (name == null || "".equals(name)) {
            name = field.getName();
        }
        xml.append("<").append(name);
        //4.判断属性是否设置命名空间
        if (!"".equals(xmlBeanElement.namespace()) && xmlBeanElement.namespace() != null) {
            xml.append(" ").append(xmlBeanElement.namespace());
        }
        xml.append(">");
        //5.判断是否字段是否被@XmlCDATA注解修饰
        if (field.isAnnotationPresent(XmlCDATA.class)) {
            //对内容进行包裹
            xml.append("<![CDATA[").append(val).append("]]>");
        }
        //6.获取对象字段的class对象,对bean对象的属性进行组装,如果字段中还含有bean对象则进行递归处理,依次类推
        Class<?> fieldClass = field.getType();
        element(xml, fieldClass, val);
        //7.拼接bean对象结束标签
        xml.append("</").append(xmlBeanElement.name()).append(">");
    }

    /**
     * 基本类型字段内容处理
     *
     * @param field
     * @param xml
     * @param val
     */

    private static void propertyElement(Field field, StringBuffer xml, Object val) {
        //1.获取@XmlPropertyELement
        XmlPropertyELement xmlPropertyELement = field.getAnnotation(XmlPropertyELement.class);
        //2/判断是否解析空值,如果不解析且值为空则返回
        if (!xmlPropertyELement.analysisEmpty() && isEmpty(val)) {
            return;
        }
        //3.属性标签名称拼接
        //如果别名为空则已字段名进行拼接
        String name = xmlPropertyELement.name();
        if (name == null || "".equals(name)) {
            name = field.getName();
        }
        xml.append("<").append(name);
        //4.判断属性是否设置命名空间,如果存在则进行拼接
        if (!"".equals(xmlPropertyELement.namespace()) && xmlPropertyELement.name() != null) {
            xml.append(" ").append(xmlPropertyELement.namespace());
        }
        xml.append(">");
        /**
         *
         *   <![CDATA[
         * 这里可以把你输入的字符原样显示,不会解析 xml
         *   ]]>
         */

        //5.拼接属性标签内容
        if (field.isAnnotationPresent(XmlCDATA.class)) {
            //如果字段被@XmlCDATA注解就用<![CDATA[标签内容]]>将标签体进行包裹
            xml.append("<![CDATA[").append(val).append("]]>");
        } else {
            //如果字段没有被@XmlCDATA注解修饰则直接拼接标签体
            xml.append(val);
        }
        //6.拼接属性标签结束标记
        xml.append("</").append(xmlPropertyELement.name()).append(">");
    }

    /**
     * 判断对象值是否为空
     *
     * @param val
     * @return
     */

    private static boolean isEmpty(Object val) {
        return val == null || "".equals(val);
    }

    /**
     * 反射获取字段值
     *
     * @param field
     * @param obj
     * @return
     */

    private static Object getFieldVal(Field field, Object obj) {
        try {
            //允许调用私有的方法
            field.setAccessible(true);
            //反射获取字段值
            return field.get(obj);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}

4.测试使用

1.使用步骤

1.首先我们需要用注解去修饰含有xml字段的bean

test01—FileDto

@Data
@XmlRootElement(name = "file",
        namespace = {"xmlns="urn:gsma:params:xml:ns:rcs:rcs:fthttp"",
                "xmlns:x="urn:gsma:params:xml:ns:rcs:rcs:up:fthttpext""})
public class FileDto {

    @XmlBeanElement(name = "file-info" ,namespace = "type="thumbnail"")
    private FileInfoDto thumbFileInfoDto;

    @XmlBeanElement(name = "file-info", namespace = "file-disposition="file"")
    private FileInfoDto fileInfoDto;

}
@Data
public class FileInfoDto {

    @XmlPropertyELement(name = "file-size")
    private String fileSize;

    @XmlPropertyELement(name = "file-name")
    private String fileName;

    @XmlPropertyELement(name = "content-type")
    private String contentType;

    @XmlPropertyELement(name = "x:branded-url")
    private String brandedUrl;

}

test02—OutboundMessageRequestDto

/**
 * <?xml version="1.0" encoding="UTF-8"?>
 * <msg:outboundMessageRequest xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
 * <messageId>5eae954c-42ca-4181-9ab4-9c0ef2e2ac66</messageId>
 *     <clientCorrelator>567895</clientCorrelator>
 * </msg:outboundMessageRequest>
 */

@Data
@XmlRootElement(name = "msg:outboundMessageRequest",namespace = "xmlns:msg="urn:oma:xml:rest:netapi:messaging:1"")
public class OutboundMessageRequestDto {

    @XmlPropertyELement(name = "messageId")
    private String messageId;

    @XmlPropertyELement(name = "clientCorrelator")
    private String clientCorrelator;

    @XmlCDATA
    @XmlPropertyELement(name = "bodyText",analysisEmpty = true)
    private String bodyText;
}

test03-RequestDto

@Data
@XmlRootElement(name = "Request",namespace = "xmlns:msg="urn:oma:xml:rest:netapi:messaging:1"")
public class RequestDto {

    @XmlPropertyELement(name = "address")
    private String address;

    @XmlGatherElement(name = "destinationAddress",childName = "phone")
    private List<String> destinationAddress;

    @XmlPropertyELement(name = "senderAddress")
    private String senderAddress;

    @XmlBeanElement(name = "Message")
    private MessageDto messageDto;

    @XmlPropertyELement(name = "clientCorrelator")
    private String clientCorrelator;

}
@Data
public class MessageDto {

    @XmlPropertyELement(name = "contentType")
    private String contentType;

    @XmlPropertyELement(name = "conversationID")
    private String conversationID;

    @XmlPropertyELement(name = "contributionID")
    private String contributionID;

    @XmlGatherElement(name = "reportRequest",showName = true,analysisEmpty = true)
    private List<String> reportRequest;

    @XmlBeanElement(name = "serviceCapability")
    private ServiceCapabilityDto serviceCapabilityDto;

    @XmlPropertyELement(name = "messageId")
    private String messageId;

    @XmlPropertyELement(name = "bodyText")
    private String bodyText;
}
@Data
public class ServiceCapabilityDto {

    @XmlPropertyELement(name = "capabilityId")
    private String capabilityId;

    @XmlPropertyELement(name = "version")
    private String version;
}

2.测试案例

public class MyTest {

    /**
     一个XML文件分为如下几部分内容
     1.文档声明
     2.元素
     3.属性
     4.注释
     5.CDATA区、特殊字符
     * <?xml version="1.0" encoding="UTF-8"?>
     * <file
     *     xmlns="urn:gsma:params:xml:ns:rcs:rcs:fthttp"
     *     xmlns:x="urn:gsma:params:xml:ns:rcs:rcs:up:fthttpext">
     *     <file-info type="thumbnail">
     *         <file-size>2048</file-size>
     *         <content-type>png</content-type>
     *     </file-info>
     *     <file-info type="file" file-disposition="file">
     *         <file-size>1024</file-size>
     *         <file-name>1.jpg</file-name>
     *         <content-type>jpeg</content-type>
     *         <x:branded-url>[alternative branded HTTP URL of the file]</x:branded-url>
     *     </file-info>
     * </file>
     */

    @Test
    public void test01(){
        FileDto fileDto = new FileDto();

        FileInfoDto thumbFileInfo = new FileInfoDto();
        thumbFileInfo.setFileSize("2048");
        thumbFileInfo.setContentType("png");
        FileInfoDto fileInfoDto = new FileInfoDto();
        fileInfoDto.setFileSize("1024");
        fileInfoDto.setFileName("1.jpg");
        fileInfoDto.setContentType("jpeg");

        fileDto.setThumbFileInfoDto(thumbFileInfo);
        fileDto.setFileInfoDto(fileInfoDto);

        String xml = XmlUtil.generateXml(fileDto);
        System.out.println(xml);
    }

    /**
     * <?xml version=v1.0 encoding=utf-8?>
     * <msg:outboundMessageRequest
     *     xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
     *     <messageId>c8c43a68-c7dd-44c6-b047-2e7e43d5d1b9</messageId>
     *     <clientCorrelator>123456</clientCorrelator>
     *     <bodyText>
     *         <![CDATA[null]]>
     *     </bodyText>
     * </msg:outboundMessageRequest>
     */

    @Test
    public void test02(){
        OutboundMessageRequestDto outboundMessageRequestDto = new OutboundMessageRequestDto();
        outboundMessageRequestDto.setMessageId(UUID.randomUUID().toString());
        outboundMessageRequestDto.setClientCorrelator("123456");
        outboundMessageRequestDto.setBodyText(null);
        String xml = XmlUtil.generateXml(outboundMessageRequestDto);
        System.out.println(xml);
    }

    /**
     * <?xml version="1.0" encoding="UTF-8"?>
     * <Request
     *     xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">
     *     <address>tel:+8619585550103</address>
     *     <destinationAddress>
     *         <phone>tel:+8619585550103</phone>
     *         <phone>tel:+8619585550104</phone>
     *     </destinationAddress>
     *     <senderAddress>sip:12599@botplatform.rcs.chinamobile.com</senderAddress>
     *     <Message>
     *         <contentType> text/plain
     *         </contentType>
     *         <conversationID>XSFDEREW#%$^$%^^&^%
     *         </conversationID>
     *         <contributionID>SFF$#%$%%^%&^%THT
     *         </contributionID>
     *         <reportRequest>Delivered</reportRequest>
     *         <reportRequest>Failed</reportRequest>
     *         <serviceCapability>
     *             <capabilityId>ChatbotSA</capabilityId>
     *             <version>+g.gsma.rcs.botversion=&quot;#=1&quot;</version>
     *         </serviceCapability>
     *         <messageId>5eae954c-42ca-4181-9ab4-9c0ef2e2ac66</messageId>
     *         <bodyText>Hello
     *         </bodyText>
     *     </Message>
     *     <clientCorrelator>567895</clientCorrelator>
     * </Request>
     */

    @Test
    public void test03(){
        RequestDto requestDto = new RequestDto();
        requestDto.setAddress("tel:+8619585550103");
        List<String> destinationAddress = new ArrayList<String>(2);
        destinationAddress.add("tel:+8619585550103");
        destinationAddress.add("tel:+8619585550104");
        requestDto.setDestinationAddress(destinationAddress);
        requestDto.setSenderAddress("sip:12599@botplatform.rcs.chinamobile.com");

        MessageDto messageDto = new MessageDto();
        messageDto.setContentType("text/plain");
        messageDto.setConversationID(UUID.randomUUID().toString());
        messageDto.setContributionID(UUID.randomUUID().toString());
        List<String> reportRequest = null;
//        reportRequest.add("Delivered");
//        reportRequest.add("Failed");
        messageDto.setReportRequest(reportRequest);

        ServiceCapabilityDto serviceCapabilityDto = new ServiceCapabilityDto();
        serviceCapabilityDto.setCapabilityId("ChatbotSA");
        serviceCapabilityDto.setVersion("+g.gsma.rcs.botversion=&quot;#=1&quot;");
        messageDto.setServiceCapabilityDto(serviceCapabilityDto);
        messageDto.setMessageId(UUID.randomUUID().toString());
        messageDto.setBodyText("Hello");

        requestDto.setMessageDto(messageDto);
        requestDto.setClientCorrelator("567895");

        String xml = XmlUtil.generateXml(requestDto);
        System.out.println(xml);
    }
}

3.测试结果

test01

<?xml version=v1.0 encoding=utf-8?>
<file
    xmlns="urn:gsma:params:xml:ns:rcs:rcs:fthttp"
    xmlns:x="urn:gsma:params:xml:ns:rcs:rcs:up:fthttpext">

    <file-info type="thumbnail">
        <file-size>2048</file-size>
        <content-type>png</content-type>
    </file-info>
    <file-info file-disposition="file">
        <file-size>1024</file-size>
        <file-name>1.jpg</file-name>
        <content-type>jpeg</content-type>
    </file-info>
</file>

test02

<?xml version=v1.0 encoding=utf-8?>
<msg:outboundMessageRequest
    xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">

    <messageId>50e1ba7a-dbb5-4f2f-8811-d81859d029ff</messageId>
    <clientCorrelator>123456</clientCorrelator>
    <bodyText>
        <![CDATA[null]]>
    </bodyText>
</msg:outboundMessageRequest>

test03

<?xml version=v1.0 encoding=utf-8?>
<Request
    xmlns:msg="urn:oma:xml:rest:netapi:messaging:1">

    <address>tel:+8619585550103</address>
    <destinationAddress>
        <phone>tel:+8619585550103</phone>
        <phone>tel:+8619585550104</phone>
    </destinationAddress>
    <senderAddress>sip:12599@botplatform.rcs.chinamobile.com</senderAddress>
    <Message>
        <contentType>text/plain</contentType>
        <conversationID>3869f2e2-aedf-424f-8928-3082dc284104</conversationID>
        <contributionID>f9d6b351-a245-4e79-b850-0700114361dd</contributionID>
        <reportRequest></reportRequest>
        <serviceCapability>
            <capabilityId>ChatbotSA</capabilityId>
            <version>+g.gsma.rcs.botversion=&quot;#=1&quot;</version>
        </serviceCapability>
        <messageId>35a43af0-df1d-4a0d-94a0-60492e520bc4</messageId>
        <bodyText>Hello</bodyText>
    </Message>
    <clientCorrelator>567895</clientCorrelator>
</Request>


原文始发于微信公众号(爱笑的boy):基于注解生成xml

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/62539.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!