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

论坛 期权论坛 编程之家     
选择匿名的用户   2021-5-21 12:19   11   0
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);
 }
 
}




分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP