在业务需要的情况下,偶尔需要使用到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);
得到的结果
public class TextMessage {private String toUserName;
private String fromUserName;
private Long createTime;
private String msgType;
private Long msgId;
private String content;
/*此处省略了getter && setter*/
}0
总结:
根据NameCoder接口实现,我们可以把驼峰式命名修改为匹配命名规则啦,比如下划线、中线、全大写下划线等,再也不用手拼字符串啦。
还没有评论,来说两句吧...