在android需要大量使用xml进行网络传输时,如何用面向对象的思路降低xml的耦合性并增加重用性

根据一个需求来进行分析。在项目中由于需要经常通过发送接收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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值