Java关于xml序列化的自定义字段名称转换

业务需要的情况下,偶尔需要使用到xml的序列化,如接入微信公众号时,推送的消息都是xml格式。有部分同学使用的是手动拼写xml,这个感觉不太爽,还是喜欢序列化工具,本文推荐使用XStream;

话不多说,开始第一步引入包(使用的Maven)

        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.7</version>
        </dependency>

然后我们创建一个简单实体对象,在创建时,字段遵从驼峰式,如下

public class TextMessage {
    private String toUserName;
    private String fromUserName;
    private Long createTime;
    private String msgType;
    private Long msgId;
    private String content;
    /*此处省略了getter && setter*/
}

xstream使用时,先创建对象如

 TextMessage msg = new TextMessage();
        msg.setContent("content");
        msg.setFromUserName("from");
        msg.setToUserName("to");
        msg.setCreateTime(1L);
        msg.setMsgType("text");
        msg.setMsgId(1L);

XStream xstream=XStream();
xstream.toXML(msg);

得到的结果是

<com.hanson.pojo.TextMessage>
  <toUserName>to</toUserName>
  <fromUserName>from</fromUserName>
  <createTime>1</createTime>
  <msgType>text</msgType>
  <msgId>1</msgId>
  <content>content</content>
</com.hanson.pojo.TextMessage>

可以看到有两点不符合微信的要求:

1)根节点是class的全路径,期望是xml  ;

2)元素节点名首字母需要大写(当前是声明的字段驼峰式)

针对第一步,我们可以使用XStream的alias,如

 xstream.alias("xml", msg.getClass());
 xstream.toXML(msg);

结果为

<xml>
  <toUserName>to</toUserName>
  <fromUserName>from</fromUserName>
  <createTime>1</createTime>
  <msgType>text</msgType>
  <msgId>1</msgId>
  <content>content</content>
</xml>

下面需要修改元素节点的首字母,查找源码可以看到XStream的构造函数可传入HierarchicalStreamDriver,而实现这个接口可以传入NameCoder,如下接口描述

public interface NameCoder {
    /**
     * Encode an object name for a node in the target format.
     * 
     * @param name the name of the object data
     * @return the node name in the target format
     * @since 1.4
     */
    String encodeNode(String name);

    /**
     * Encode a meta-data name for an attribute in the target format.
     * 
     * @param name the name of the meta-data
     * @return the attribute name in the target format
     * @since 1.4
     */
    String encodeAttribute(String name);

    /**
     * Decode a node name to an object name.
     * 
     * @param nodeName the name of the node
     * @return the name of the object
     * @since 1.4
     */
    String decodeNode(String nodeName);

    /**
     * Decode an attribute name to an object name.
     * 
     * @param attributeName the name of the attribute
     * @return the name of the meta-data
     * @since 1.4
     */
    String decodeAttribute(String attributeName);
}

从这个接口可以看出,可以实现此接口进行节点转换,完整代码如下

public class FieldNameUtil {
    /**
     * 分割驼峰字段
     * @param name
     * @param separator
     * @return
     */
    private static String separateCamelCase(String name, String separator) {
        StringBuilder translation = new StringBuilder();
        for (int i = 0; i < name.length(); i++) {
            char character = name.charAt(i);
            if (Character.isUpperCase(character) && translation.length() != 0) {
                translation.append(separator);
            }
            translation.append(character);
        }
        return translation.toString();
    }
    /**
     * 下划线转换为驼峰
     */
    public static String underscore2CamelCase(String name) {
        StringBuilder translation = new StringBuilder();
        for (int i = 0; i < name.length(); i++) {
            char character = name.charAt(i);
            if (character == '_')
                continue;
            if (translation.length() != 0 && name.charAt(i - 1) == '_') {
                translation.append(Character.toUpperCase(character));
            } else {
                translation.append(character);
            }
        }
        return translation.toString();
    }

    /**
     * 驼峰转换为下划线
     * @param name
     * @return
     */
    public static String camelCase2Underscore(String name) {
        return separateCamelCase(name, "_").toLowerCase();
    }

    /**
     * 首字母大写
     * @param name
     * @return
     */
    public static String upperCaseFirstLetter(String name){
        StringBuilder fieldNameBuilder = new StringBuilder();
        int index = 0;
        char firstCharacter = name.charAt(index);
        while (index < name.length() - 1) {
            if (Character.isLetter(firstCharacter)) {
                break;
            }

            fieldNameBuilder.append(firstCharacter);
            firstCharacter = name.charAt(++index);
        }

        if (index == name.length()) {
            return fieldNameBuilder.toString();
        }

        if (!Character.isUpperCase(firstCharacter)) {
            String modifiedTarget = modifyString(Character.toUpperCase(firstCharacter), name, ++index);
            return fieldNameBuilder.append(modifiedTarget).toString();
        } else {
            return name;
        }
    }

    /**
     * 首字母小写
     * @param name
     * @return
     */
    public static String lowerCaseFirstLetter(String name){
        StringBuilder fieldNameBuilder = new StringBuilder();
        int index = 0;
        char firstCharacter = name.charAt(index);
        while (index < name.length() - 1) {
            if (Character.isLetter(firstCharacter)) {
                break;
            }

            fieldNameBuilder.append(firstCharacter);
            firstCharacter = name.charAt(++index);
        }

        if (index == name.length()) {
            return fieldNameBuilder.toString();
        }

        if (!Character.isLowerCase(firstCharacter)) {
            String modifiedTarget = modifyString(Character.toLowerCase(firstCharacter), name, ++index);
            return fieldNameBuilder.append(modifiedTarget).toString();
        } else {
            return name;
        }
    }

    /**
     * 修改字符串
     * @param firstCharacter
     * @param srcString
     * @param indexOfSubstring
     * @return
     */
    private static String modifyString(char firstCharacter, String srcString, int indexOfSubstring) {
        return (indexOfSubstring < srcString.length())
                ? firstCharacter + srcString.substring(indexOfSubstring)
                : String.valueOf(firstCharacter);
    }
}
import com.thoughtworks.xstream.io.naming.NameCoder;

public class UpperCaseNameCoder implements NameCoder {
    public String encodeNode(String name) {
        return name.equalsIgnoreCase("xml")?"xml":FieldNameUtil.upperCaseFirstLetter(name);
    }

    public String encodeAttribute(String name) {
        return name.equalsIgnoreCase("xml")?"xml":FieldNameUtil.upperCaseFirstLetter(name);
    }

    public String decodeNode(String nodeName) {
        return FieldNameUtil.lowerCaseFirstLetter(nodeName);
    }

    public String decodeAttribute(String attributeName) {
        return FieldNameUtil.lowerCaseFirstLetter(attributeName);
    }
}

使用是,直接使用

 xstream=new XStream(new DomDriver("UTF-8", new UpperCaseNameCoder()));
 xstream.alias("xml", textMsg);
 xstream.toXML(obj);

得到的结果

<xml>
  <ToUserName>to</ToUserName>
  <FromUserName>from</FromUserName>
  <CreateTime>1</CreateTime>
  <MsgType>text</MsgType>
  <MsgId>1</MsgId>
  <Content>content</Content>
</xml>

总结:

根据NameCoder接口实现,我们可以把驼峰式命名修改为匹配命名规则啦,比如下划线、中线、全大写下划线等,再也不用手拼字符串啦。

 

posted @ 2018-04-03 19:07  HansonYao  阅读(2064)  评论(0编辑  收藏  举报