根据一个需求来进行分析。在项目中由于需要经常通过发送接收xml进行网络通信,从服务器中抓取数据
如果不对xml做优化,需要写大量的serializer.startTag(null, "header");serializer.endTag(null, "header"); ......如何进行优化呢.
我们来看这个需求,这是一个按照文档写出的xml文件作为发送方,
<?xml version=”1.0” encoding=”utf-8”?>
<message version="1.0">
<header>
<agenterid>889931</agenterid>
<source>ivr</source>
<compress>DES</compress>
<messengerid>20131013101533000001</messengerid>
<timestamp>20131013101533</timestamp>
<digest>7ec8582632678032d25866bd4bce114f</digest>
<transactiontype>12002</transactiontype>
<username>13200000000</username>
</header>
<body>
<elements>
<element>
<lotteryid>118</lotteryid>
<issues>1</issues>
</element>
</elements>
</body>
</message>
里面的节点意义我就不说了,header表示请求的协议,一般不会变,只有messageid ,timestamp digest 会有规律的变化,username 和 transactiontype会无规律的变化,body中的elements元素需要进行加密处理,处理完的样子时<body>sdfsfsdasd34rnh34u53434534534o</body> 是进行des加密的
下面我们根据面向对象的方式来对这个xml进行优化。首先分析这个xml
好的,下面我们开始写简单的叶子和值
package com.jrrjw.caipiao.net.protocal;
import org.xmlpull.v1.XmlSerializer;
/**
* 简单的叶子和值 <name>value</name>
*
* @author Idea
*
*/
public class Leaf {
private String tagName;
private String tagValue;
public void setTagValue(String tagValue) {
this.tagValue = tagValue;
}
/**
* 对于一个xml 节点的名称时必须的
*
* @param tagName
*/
public Leaf(String tagName) {
this.tagName = tagName;
}
public Leaf(String tagName, String tagValue) {
this.tagName = tagName;
this.tagValue = tagValue;
}
/**
* ͨ通过调用它 的对象提供serializer序列化节点
*
* @param serializer
*/
public void serializerLeaf(XmlSerializer serializer) {
try {
serializer.startTag(null, tagName);
if (tagValue == null) {
tagValue = "";
}
serializer.text(tagValue);
serializer.endTag(null, tagName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面再开始写header
package com.jrrjw.caipiao.net.protocal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
import org.apache.commons.codec.digest.DigestUtils;
import org.xmlpull.v1.XmlSerializer;
import com.jrrjw.caipiao.ConstantValue;
public class Header {
// 这些都是规定好的协议头
// <agenterid>889931</agenterid>
private Leaf agenterid = new Leaf("agenterid", ConstantValue.AGENTERID);
// <source>ivr</source>
private Leaf source = new Leaf("source", ConstantValue.SOURCE);
// <compress>DES</compress>
private Leaf compress = new Leaf("compress", ConstantValue.COMPRESS);
// <messengerid>20131013101533000001</messengerid>
private Leaf messengerid = new Leaf("messengerid");
// <timestamp>20131013101533</timestamp>
private Leaf timestamp = new Leaf("timestamp");
// <digest>7ec8582632678032d25866bd4bce114f</digest>
private Leaf digest = new Leaf("digest");
// <transactiontype>12002</transactiontype>
private Leaf transactiontype = new Leaf("transactiontype");
// <username>13200000000</username>
private Leaf username = new Leaf("username");
/**
* 由调用者提供序列化对象,由于header里的都是固定好的,这里就不用抽取了。
*
* @param serializer
*/
public void serializerHeader(XmlSerializer serializer, String body) {
// 首先保证上面实例化的叶子都有tagValue值
// messengerid 时间戳+随机6位数
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(
"yyyyMMddHHmmss");
String time = simpleDateFormat.format(new Date());
Random random = new Random();
int num = random.nextInt(999999) + 1;
messengerid.setTagValue(time + num);
timestamp.setTagValue(time);
// digest 由時間戳+密码+body原文 然后进行md5加密
StringBuffer buffer = new StringBuffer();
buffer.append(time);
buffer.append(ConstantValue.AGENTER_PASSWORD);
buffer.append(body);
String v = DigestUtils.md5Hex(buffer.toString());
digest.setTagValue(v);
try {
serializer.startTag(null, "header");
agenterid.serializerLeaf(serializer);
source.serializerLeaf(serializer);
compress.serializerLeaf(serializer);
messengerid.serializerLeaf(serializer);
timestamp.serializerLeaf(serializer);
digest.serializerLeaf(serializer);
transactiontype.serializerLeaf(serializer);
username.serializerLeaf(serializer);
serializer.endTag(null, "header");
} catch (Exception e) {
e.printStackTrace();
}
}
public Leaf getTransactiontype() {
return transactiontype;
}
public Leaf getUsername() {
return username;
}
}
header写完,下面开始写body部分了,首先考虑一下这个element的写法,看代码,看到最后你就知道为什么这么写了。
package com.jrrjw.caipiao.net.protocal;
import org.xmlpull.v1.XmlSerializer;
/**
* 存放叶子的集合,做成抽象类,由子类自定义实现
* @author Idea
*
*/
public abstract class Element {
/**
* 每个element都需要序列化
* @param serializer
*/
public abstract void serializerElement(XmlSerializer serializer);
/**
* 每一个Element都需要唯一标示符
* @return
*/
public abstract String getTransactionType();
}
下面开始写body
package com.jrrjw.caipiao.net.protocal;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.xmlpull.v1.XmlSerializer;
import android.util.Xml;
import com.jrrjw.caipiao.ConstantValue;
import com.jrrjw.caipiao.utils.DES;
public class Body {
private List<Element> elements = new ArrayList<Element>();
public void serializerBody(XmlSerializer serializer) {
try {
serializer.startTag(null, "body");
serializer.startTag(null, "elements");
if (elements != null) {
for (Element element : elements) {
element.serializerElement(serializer);
}
}
serializer.endTag(null, "elements");
serializer.endTag(null, "body");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public List<Element> getElements() {
return elements;
}
/**
* 获取body里面的DES加密数据,为了安全需要加密,这段可以在JNI里实现,这里简单起见
* @return
*/
public String getBodyInsideDESInfo()
{
// 加密数据
String wholeBody = getWholeBody();
String orgDesInfo=StringUtils.substringBetween(wholeBody, "<body>", "</body>");
// 加密
// 加密调试——2天
// ①加密算法实现不同
// ②加密的原始数据不同
DES des=new DES();
return des.authcode(orgDesInfo, "DECODE", ConstantValue.DES_PASSWORD);
}
/**
* 由于需求说明,这个方法的目的是在header里提供一个body的未加密数据在digest中
* @return
*/
public String getWholeBody() {
try {
// 这里不能用XmlSerializer serializer 至于为什么自己考虑一下就明白了
XmlSerializer temp = Xml.newSerializer();
StringWriter writer = new StringWriter();
temp.setOutput(writer);
temp.startTag(null, "body");
temp.startTag(null, "elements");
if (elements != null) {
for (Element element : elements) {
element.serializerElement(temp);
}
}
temp.endTag(null, "elements");
temp.endTag(null, "body");
//因为这个temp并没有startDocument 所以他不会自动写闭合标签,也就不会自动关闭,所以要手动刷一下
temp.flush();
return writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
最后写Message
package com.jrrjw.caipiao.net.protocal;
import java.io.StringWriter;
import org.xmlpull.v1.XmlSerializer;
import android.util.Xml;
import com.jrrjw.caipiao.ConstantValue;
/**
* 这里实现整个xml的框架
* @author Idea
*
*/
public class Messager {
private Header header = new Header();
private Body body = new Body();
public Header getHeader() {
return header;
}
public Body getBody() {
return body;
}
/**
* 序列化协议
*/
public void serializerMessage(XmlSerializer serializer) {
try {
// <message version="1.0">
serializer.startTag(null, "message");
// MUST follow a call to startTag() immediately
serializer.attribute(null, "version", "1.0");
header.serializerHeader(serializer, body.getWholeBody());// 获取完整的body
// body.serializerBody(serializer);这里就没有调用这个方法,
serializer.startTag(null, "body");
serializer.text(body.getBodyInsideDESInfo());
serializer.endTag(null, "body");
serializer.endTag(null, "message");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取请求的xml文件
*
* @return
*/
public String getXml(Element element) {
if (element == null) {
throw new IllegalArgumentException("element is null");
}
// 请求标示需要设置,请求内容需要设置
header.getTransactiontype().setTagValue(element.getTransactionType());
body.getElements().add(element);
// 序列化
XmlSerializer serializer = Xml.newSerializer();
StringWriter writer = new StringWriter();
// This method can only be called just after setOutput
try {
serializer.setOutput(writer);
serializer.startDocument(ConstantValue.ENCONDING, null);
this.serializerMessage(serializer);
serializer.endDocument();
return writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
xml已经基本抽象出来可以作为通用了,那么我们怎么用呢。
package com.jrrjw.caipiao.net.protocal.element;
import org.xmlpull.v1.XmlSerializer;
import com.jrrjw.caipiao.net.protocal.Element;
import com.jrrjw.caipiao.net.protocal.Leaf;
/**
* 这个类的目的时定义body体的内容
* @author Idea
*
*/
public class TestElement extends Element{
//可以赋值,这样做更好
private Leaf lotteryid = new Leaf("lotteryid");
private Leaf issues = new Leaf("issues","1");
public Leaf getLotteryid() {
return lotteryid;
}
// public Leaf getIssues() {
// return issues;
// }
@Override
public void serializerElement(XmlSerializer serializer) {
try {
serializer.startTag(null, "element");
lotteryid.serializerLeaf(serializer);
issues.serializerLeaf(serializer);
serializer.endTag(null, "element");
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public String getTransactionType() {
return "121212";
}
}
最后看看测试结果
package com.jrrjw.caipiao.test;
import android.test.AndroidTestCase;
import com.jrrjw.caipiao.net.protocal.Messager;
import com.jrrjw.caipiao.net.protocal.element.TestElement;
public class XmlTest extends AndroidTestCase{
public void test1(){
TestElement element = new TestElement();
element.getLotteryid().setTagValue("118");
Messager messager = new Messager();
messager.getHeader().getUsername().setTagValue("idea");
String value = messager.getXml(element);
System.out.println(value);
}
}
这里用的android测试需要加上
<instrumentation android:targetPackage="com.jrrjw.caipiao" android:name="android.test.InstrumentationTestRunner" />
<uses-library android:name="android.test.runner" />
这里只是把发送的内容抽象了出来,也可以把接收的内容抽象到一起,其实很简单,这里就不写了。
用到的des加密工具类
package com.jrrjw.caipiao.utils;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class DES {
public DES() {
}
public static DES getInstance(String key) throws NoSuchPaddingException,
NoSuchAlgorithmException {
return getInstance(getKeyByStr(key));
}
public static DES getInstance(byte key[]) throws NoSuchPaddingException,
NoSuchAlgorithmException {
DES des = new DES();
if (des.key == null) {
SecretKeySpec spec = new SecretKeySpec(key, "DES");
des.key = spec;
}
des.cipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
return des;
}
public byte[] encrypt(byte b[]) throws InvalidKeyException,
BadPaddingException, IllegalBlockSizeException,
IllegalStateException {
byte byteFina[] = null;
cipher.init(1, key);
byteFina = cipher.doFinal(b);
return byteFina;
}
public byte[] decrypt(byte b[]) throws InvalidKeyException,
BadPaddingException, IllegalBlockSizeException,
IllegalStateException {
byte byteFina[] = null;
cipher.init(2, key);
byteFina = cipher.doFinal(b);
return byteFina;
}
public static byte[] getKeyByStr(String str) {
byte bRet[] = new byte[str.length() / 2];
for (int i = 0; i < str.length() / 2; i++) {
Integer itg = new Integer(16 * getChrInt(str.charAt(2 * i))
+ getChrInt(str.charAt(2 * i + 1)));
bRet[i] = itg.byteValue();
}
return bRet;
}
private static int getChrInt(char chr) {
int iRet = 0;
if (chr == "0".charAt(0))
iRet = 0;
if (chr == "1".charAt(0))
iRet = 1;
if (chr == "2".charAt(0))
iRet = 2;
if (chr == "3".charAt(0))
iRet = 3;
if (chr == "4".charAt(0))
iRet = 4;
if (chr == "5".charAt(0))
iRet = 5;
if (chr == "6".charAt(0))
iRet = 6;
if (chr == "7".charAt(0))
iRet = 7;
if (chr == "8".charAt(0))
iRet = 8;
if (chr == "9".charAt(0))
iRet = 9;
if (chr == "A".charAt(0))
iRet = 10;
if (chr == "B".charAt(0))
iRet = 11;
if (chr == "C".charAt(0))
iRet = 12;
if (chr == "D".charAt(0))
iRet = 13;
if (chr == "E".charAt(0))
iRet = 14;
if (chr == "F".charAt(0))
iRet = 15;
return iRet;
}
private Key key;
private Cipher cipher;
/**
* @interpret 进行base64加密操作
* @param text
* @param keyString
* @return String
*/
public String encrypt(String text, String keyString) {
String body = null;
try {
DES des = DES.getInstance(keyString);
byte[] b = des.encrypt(text.getBytes("UTF8"));
body = new String(Base64.encodeBase64(b));
} catch (Exception ex) {
}
return body;
}
/**
* @interpret 进行base64进行解密
* @param text
* @param keyString
* @return String
*/
public String decrypt(String text, String keyString) {
String body = null;
try {
DES des = DES.getInstance(keyString);
byte[] b = Base64.decodeBase64(text.getBytes());
body = new String(des.decrypt(b), "UTF8");
} catch (Exception e) {
e.printStackTrace();
}
return body;
}
/**
*
* @param content 内容
* @param operation 加密或解密
* @param key 使用到的密钥:固定长度
* @return
*/
public String authcode(String content, String operation, String key){
String encontent = null;
if (operation != null && operation.equals("DECODE")) {
encontent = encrypt(content,key);
} else if (operation != null && operation.equals("ENCODE")) {
encontent = decrypt(content,key);
}
return encontent;
}
public static void main(String[] args) {
DES des = new DES();
System.out.println(des.authcode("wewweewewew=","DECODE","0102030405060708"));//加密
System.out.println(des.authcode("d8fPhfd9JkW99p8aqhtVIA==","ENCODE","0102030405060708"));//解密
}
}
用到的jar包
commons-codec.jar
commons-lang3-3.0-beta.jar