基于jackson的json key压缩工具类

Bean类

@Getter
@Setter
@JsonZipKey.Id(value = "3", enableKeyMap = true)
public class Body3 {

    @JsonZipKey.KeyMap("a")
    private String ati;

    @JsonZipKey.KeyMap("b")
    private Long uid;

    @JsonZipKey.KeyMap("c")
    private String type;

    @JsonZipKey.KeyMap("d")
    private String city;

    @JsonZipKey.KeyMap("e")
    private String json;

    @JsonZipKey.KeyMap("f")
    private Integer bit;

    @JsonZipKey.KeyMap("g")
    private String date;

}

Util 类

package com.zipkey;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
* 使用方式:
* 1 调用 getJSONZipMap(Body.class, null, "1", false, true) 生成 key 到 short-key 的映射;
* 用于服务器端将 json 的 key 转为 short-key
* 2 调用 getJSONZipMap(Body.class, null, "1", true, false) 生成 short-key 到 key 的映射;
* 用于客户端将将 short-key 还原回原来的 key
* 3 通过id区分到底是哪个类的映射,id通过特殊字段 - 存储;同时还能通过是否存在 - 来判断是否启动压缩。
* --- 通过消耗小量的CPU来减少带宽消耗,对 IM 服务器这种IO密集型应用有作用。
* 缺点:一旦增加新的字段压缩就可能会发生客户端解析错误,修改字段的时候记得更改id的值 或者 字段的字母顺序是最后一个。
* 使用建议:每个字段显性增加一个 前缀排序 如 axxx 按从小到大的顺序进行
*/
public class JsonZipKey {

private static final char[] keyList = new char[52];

static {
int total = 0;
for(int c = 'a'; c <= 'z'; c++){
keyList[total++] = (char) c;
}
for(int c = 'A'; c <= 'Z'; c++){
keyList[total++] = (char) c;
}
}

public static <T> ObjectNode getJSONZipMap(Class<T> cl, Object obj, boolean shortToLong) throws Exception {
obj = obj != null ? obj : cl.newInstance();
JsonNode json = JsonUtil.objToJsonNode(obj);
ObjectNode jsonNode = JsonUtil.createObject();
Iterator<String> it = json.fieldNames();
int i = 0;
while(it.hasNext()) {
String k = it.next();
if(shortToLong) {
jsonNode.put(getZipChar(i++), k);
} else {
jsonNode.put(k, getZipChar(i++));
}
}
return jsonNode;
}

public static <T> ObjectNode getJSONZipMapAutoCheck(Class<T> cl, Object obj, boolean shortToLong) throws Exception {
Id idSet = cl.getAnnotation(Id.class);
if(idSet != null && idSet.enableKeyMap()) {
return getJSONZipMap(cl, shortToLong);
}
return getJSONZipMap(cl, obj, shortToLong);
}

public static <T> ObjectNode getJSONZipMap(Class<T> cl, boolean shortToLong) {
Id idSet = cl.getAnnotation(Id.class);
if(idSet == null || !idSet.enableKeyMap()) {
throw new RuntimeException("JsonZipKey.@Id not exist or map field is false!");
}
ObjectNode jsonNode = JsonUtil.createObject();
Set<String> filedSet = new HashSet<>();
for (Field field : cl.getDeclaredFields()) {
KeyMap km = field.getAnnotation(KeyMap.class);
if(km == null) {
throw new RuntimeException(field.getName() + " not exist JsonZipKey.@KeyMap");
}
String shortKey = km.value();
String k = field.getName();
//保证不会出现重复的key
if(!filedSet.add(shortKey)) {
throw new RuntimeException(k + " JsonZipKey.@KeyMap(\"" + shortKey + "\") already exist!");
}
if(shortToLong) {
jsonNode.put(shortKey, k);
} else {
jsonNode.put(k, shortKey);
}
}
jsonNode.put("-", idSet.value());
return jsonNode;
}

public static ObjectNode revert(ObjectNode source, ObjectNode map) {
ObjectNode r = JsonUtil.createObject();
source.fields().forEachRemaining(a -> {
//不处理 自定义的id记录字段。
if("-".equals(a.getKey())) return;
JsonNode sourceKey = map.get(a.getKey());
if(sourceKey == null) {
throw new RuntimeException(map + " not find field: " + a.getKey());
}
r.set(sourceKey.asText(), a.getValue());
});
return r;
}

public static ObjectNode zip(Object value) throws Exception {
Class<?> cl = value.getClass();
ObjectNode map = JsonUtil.getZipMap(cl);
if(map == null) {
synchronized (cl) {
map = JsonUtil.getZipMap(cl);
if(map == null) {
map = getJSONZipMapAutoCheck(cl, null, false);
JsonUtil.putZipMap(cl, map);
}
}
}
ObjectNode r = zip(map, JsonUtil.objToJsonNode(value));
r.set("-", map.get("-"));
return r;
}

public static ObjectNode zip(JsonNode map, JsonNode value) {
if(map == null) {
throw new RuntimeException("map must not null!");
}
ObjectNode r = JsonUtil.createObject();
value.fieldNames().forEachRemaining(a -> {
JsonNode v = value.get(a);
if(v != null && !v.isNull()) {
JsonNode field = map.get(a);
if(field == null) {
throw new RuntimeException(map + " map field not find: " + a);
}
r.set(field.asText(), v);
}
});
return r;
}

public static String getZipChar(int i) {
if(i < 52) return keyList[i] + "";
StringBuilder sb = new StringBuilder();
do {
sb.append(keyList[i % 52]);
i = i / 52;
} while (i > 52);
sb.append(keyList[i-1]);
return sb.toString();
}

public static class JsonUtil {

private static final ObjectMapper mapper = new ObjectMapper();

private static final Map<Class<?>, ObjectNode> zipMap = new ConcurrentHashMap<>();
private static final Map<String, Class<?>> zipClassMap = new ConcurrentHashMap<>();

static {
mapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
}

public static void putZipMap(Class<?> id, ObjectNode map) {
Id idInterface = id.getAnnotation(Id.class);
if(idInterface == null) {
throw new RuntimeException("please add @JsonZipKey.Id(\"xxx\") in class!");
}
String v = idInterface.value();
Class<?> zc = zipClassMap.get(v);
if(zc != null && zc != id) {
throw new RuntimeException(zc.getName() + " @JsonZipKey.Id " + v + " already exists!");
}
map.put("-", v);
zipMap.put(id, map);
zipClassMap.put(v, id);
}

public static ObjectNode getZipMap(Class<?> id) {
return zipMap.get(id);
}

public static List<ObjectNode> getAllZipMap() {
return new ArrayList<>(zipMap.values());
}

public static JsonNode objToJsonNode(Object obj) throws JsonProcessingException {
//return mapper.convertValue(obj, JsonNode.class);
return mapper.valueToTree(obj);
}

public static String toJson(Object obj) throws JsonProcessingException {
return mapper.writeValueAsString(obj);
}

public static JsonNode toJsonNode(String json) throws JsonProcessingException {
return mapper.readTree(json);
}

public static ObjectNode createObject() {
return mapper.createObjectNode();
}
}

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
String value();
boolean enableKeyMap() default false;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface KeyMap {
String value();
}

}

Test类

@Test
public void test1() throws Exception {
Body3 c = new Body3();
c.setDate("2023-01-01");
c.setUid(1L);
c.setType("11");
c.setCity("china");
c.setJson("{}");
c.setAti("active");
//直接获得压缩后的json
ObjectNode zipObject = JsonZipKey.zip(c);
System.out.println("zip: "+ zipObject);

//获取所有类的字段关联
System.out.println("zip map: " + JsonZipKey.JsonUtil.getAllZipMap());

//生成反向解析压缩字段的关联
ObjectNode revertMap = JsonZipKey.getJSONZipMapAutoCheck(Body3.class, null, true);
System.out.println("zip revert map: " + revertMap);

//反向解析压缩json
ObjectNode revertObject = JsonZipKey.revert(zipObject, revertMap);
System.out.println("zip revert: " + revertObject);

}

 

posted @ 2024-04-16 16:42  数学与IT  阅读(22)  评论(0编辑  收藏  举报