TLV(tag length value) 概念描述及实例应用

TLV(tag-length-value),即每个子域由tag标签(T),子域取值的长度(L)和子域取值(V)构成。

tag标签的属性为bit,由16进制表示,占1~2个字节长度。例如,“9F33”为一个占用两个字节的tag标签。而“95”为一个占用一个字节的tag标签。若tag标签的第一个字节(注:字节排序方向为从左往右数,第一个字节即为最左边的字节。bit排序规则同理。)的后四个bit为“1111”,则说明该tag占两个字节,例如“9F33”;否则占一个字节,例如“95”。

子域长度(即L本身)的属性也为bit,占1~3个字节长度。具体编码规则如下:

a) 当L字段最左边字节的最左bit位(即bit8)为0,表示该L字段占一个字节,它的后续7个bit位(即bit7~bit1)表示子域取值的长度,采用二进制数表示子域取值长度的十进制数。例如,某个域取值占3个字节,那么其子域取值长度表示为“00000011”。所以,若子域取值的长度在1~127字节之间,那么该L字段本身仅占一个字节。

b) 当L字段最左边字节的最左bit位(即bit8)为1,表示该L字段不止占一个字节,那么它到底占几个字节由该最左字节的后续7个bit位(即bit7~bit1)的十进制取值表示。例如,若最左字节为10000010,表示L字段除该字节外,后面还有两个字节。其后续字节的十进制取值表示子域取值的长度。例如,若L字段为“1000 0001 1111 1111”,表示该子域取值占255个字节。所以,若子域取值的长度在127~255字节之间,那么该L字段本身需占两个字节。
public class TLVUtil {
	
	/**
	 * @Title:encodeTLV
	 * @Description: 编码键值对为tlv格式字符串
	 * @param map 键值对,键为16进制字符串tag,值为16进制字符串。若原字符串为ascii字符串,需先转为16进制字符串
	 * @return String tlv字符串
	 */
	public static String encodeTLV(Map<String, String> map) {
		if (map == null) {
			return null;
		}
		StringBuilder sb = new StringBuilder();
		for (String key : map.keySet()) {
			if (key == null) {
				continue;
			}
			String tag = key;
			if (key.length() % 2 != 0) {
				key = StringUtils.leftPad(key, key.length() + 1, '0');
			}
			if (!Pattern.compile("[0-9a-fA-F]([0-9a-eA-E]|(F[0-9a-fA-F]{2}))").matcher(key).matches()) {
				throw new RuntimeException("tag[" + key + "]格式错误");
			}
			sb.append(tag);
			String hexV = map.get(key);
			if (hexV == null) {
				sb.append("00");
				continue;
			}
			byte[] vbytes = DatatypeConverter.parseHexBinary(hexV);
			if (vbytes.length > 0xFFFF) {
				throw new RuntimeException("value长度不能超过65535");
			} else if (vbytes.length > 0x7F) {
				String lstr = Integer.toHexString(vbytes.length);
				if ((lstr.length() % 2) != 0) {
					lstr = StringUtils.leftPad(lstr, lstr.length() + 1, '0');
				}
				String hlstr = Integer.toHexString(0x80 | (lstr.length() / 2));
				if ((hlstr.length() % 2) != 0) {
					hlstr = StringUtils.leftPad(hlstr, lstr.length() + 1, '0');
				}
				sb.append(hlstr).append(lstr);
			} else {
				String lstr = Integer.toHexString(vbytes.length);
				if ((lstr.length() % 2) != 0) {
					lstr = StringUtils.leftPad(lstr, lstr.length() + 1, '0');
				}
				sb.append(lstr);
			}
			sb.append(DatatypeConverter.printHexBinary(vbytes));
		}
		return sb.toString();
	}
	
	/**
	 * @Title:encodeTLV2
	 * @Description: 编码键值对为tlv格式字符串
	 * @param map 两级键值对,子父键都为16进制字符串,子级值为ascii编码
	 * @return String tlv字符串
	 */
	public static String encodeTLV2(Map<String, Map<String, String>> map) {
		if (map == null) {
			return null;
		}
		Map<String, String> tpmap = new HashMap<String, String>();
		for (String key : map.keySet()) {
			Map<String, String> tmap = map.get(key);
			Map<String, String> tcmap = new HashMap<String, String>();
			for (String ckey : tmap.keySet()) {
				if(tmap.get(ckey) == null) {
					continue;
				}
				tcmap.put(ckey, DatatypeConverter.printHexBinary(tmap.get(ckey).getBytes()));
			}
			tpmap.put(key, encodeTLV(tcmap));
		}
		return encodeTLV(tpmap);
	}
	
	/**
	 * @Title:decodeTLV
	 * @Description: 解码 TLV 16进制字符串
	 * @param tlvStr 16进制字符串
	 * @return Map<String, String> 键为tag,值为tag对应V的16进制字符串。注:V通常需要再处理(如:截取、再解码或16进制转utf8字符串等等)。
	 */
	public static Map<String, String> decodeTLV(String tlvStr) {
		Map<String, String> map = new LinkedHashMap<String,String>();
		if (StringUtils.isBlank(tlvStr)) {
			return map;
		}
		CharBuffer charBuffer = CharBuffer.wrap(tlvStr.toCharArray());
		charBuffer.mark();
		char[] tagChars = new char[2];
		charBuffer.get(tagChars);
		if ((Integer.valueOf(String.valueOf(tagChars), 16) & 0x0F) == 0x0F) {
			charBuffer.reset();
			tagChars = new char[4];
			charBuffer.get(tagChars);
		}
		char[] lenChars = new char[2];
		charBuffer.get(lenChars);
		int len = Integer.parseInt(String.valueOf(lenChars), 16);
		if (len > 0x7F) {
			lenChars = new char[len & 0x7F];
			charBuffer.get(lenChars);
			len = Integer.parseInt(String.valueOf(lenChars));
		}
		char[] valChars = new char[len * 2];
		charBuffer.get(valChars);
		map.put(String.valueOf(tagChars), String.valueOf(valChars));
		if (charBuffer.remaining() > 0) {
			char[] remainingChars = new char[charBuffer.remaining()];
			charBuffer.get(remainingChars);
			map.putAll(decodeTLV(String.valueOf(remainingChars)));
		}
		return map;
	}
	
	/**
	 * @Title:decodeTLV2
	 * @Description: 解码2级 TLV 16进制字符串
	 * @param tlvStr 16进制字符串
	 * @return Map<String,Map<String,String>> 键为tag,值为tag对应V的16进制字符串再进行tlv解析map,且子map值已将16进制转ascii码
	 */
	public static Map<String, Map<String, String>> decodeTLV2(String tlvStr) {
		Map<String, Map<String, String>> pmap  = new LinkedHashMap<String, Map<String, String>>();
		Map<String, String> tpmap = decodeTLV(tlvStr);
		for (String ptag : tpmap.keySet()) {
			Map<String, String> cmap = new LinkedHashMap<String, String>();
			Map<String, String> tcmap = decodeTLV(tpmap.get(ptag));
			for (String ctag : tcmap.keySet()) {
				if (StringUtils.isEmpty(tcmap.get(ctag))) {
					cmap.put(ctag, tcmap.get(ctag));
					continue;
				}
				cmap.put(ctag, new String(DatatypeConverter.parseHexBinary(tcmap.get(ctag))));
			}
			pmap.put(ptag, cmap);
		}
		return pmap;
	}
	
	public static void main(String[] args) {
		String str = "A32801123230313730353038363637373838393930300212323031373035303831313232333334343535";
		Map<String, Map<String, String>> m = decodeTLV2(str);
		System.out.println(JSONObject.fromObject(m).toString());
		String s = encodeTLV2(m);
		System.out.println(s);
	}
	
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值