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接口實現,我們可以把駝峰式命名修改為匹配命名規則啦,比如下划線、中線、全大寫下划線等,再也不用手拼字符串啦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM