资讯专栏INFORMATION COLUMN

Java API中Msgpack支持Object类型

邹强 / 1302人阅读

摘要:支持等众多语言。此处的是经过封装的,和中类似。相关模板均在类中定义,诸如。优化后的使用方法为中的错误先看看这段代码反序列化后获取,因为中是类型,这里面可能是,也可能是,因此需要注意。参考资料让版支持类型

Msgpack简介

MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it"s faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.

MessagePack是一个基于二进制高效的对象序列化Library用于跨语言通信。它可以像JSON那样,在许多种语言之间交换结构对象;但是它比JSON更快速也更轻巧。 支持Python、Ruby、Java、C/C++、Javascript等众多语言。 比Google Protocol Buffers还要快4倍。

Graddle依赖管理
    // msgpack
    compile "org.msgpack:msgpack:0.6.12" // 依赖javassist和json-simple
    //slf-logback
    compile "org.slf4j:jcl-over-slf4j:1.7.7"
    compile "org.slf4j:jcl-over-slf4j:1.7.7" // 依赖lf4j-api,故lf4j-api可以不多带带配置
    compile "org.slf4j:slf4j-api:1.7.7"
    // javassist
    compile "org.javassist:javassist:3.20.0-GA" // 如果其他有依赖,则可不多带带配置
    compile "com.googlecode.json-simple:json-simple:1.1.1"
使用Msgpack解析yii2 session中的数据

yii2将session数据写入redis中,为了在java api中可以使用后台用户登录的信息,则需要通过java解析session数据。
先看看如何使用:

// sessionBytes是byte类型
if (null != sessionBytes) {
    MessagePack pack = new MessagePack(); // new一个msgpack对象
    Map map = null;
    try {
        map = (Map) pack.read(sessionBytes);  // 通过Msgpack的read读取字节,强制转换为Map对象
    } catch (IOException e) {
        LOGGER.warn("get request user from redis failed: ", e);
    }

    if (!com.joyven.util.Utils.isEmpty(map)) {
        map.forEach((k, v) -> {
            String key = String.valueOf(k);
            String value = String.valueOf(v);
            if (Const.SESSION_KEY_UID.equals(key)) {
                user.setUid(Long.parseLong(trim(value, USER_STRING_REDUNDANCY)));
            }
            if (Const.SESSION_KEY_USERNAME.equals(key)) {
                user.setUsername(trim(value, USER_STRING_REDUNDANCY));
            }
            if (Const.SESSION_KEY_AVATAR.equals(key)) {
                user.setAvatar(trim(value, USER_STRING_REDUNDANCY));
            }
            if (Const.SESSION_KEY_CST.equals(key)) {
                user.setCraftsmanStatus(Integer.parseInt(trim(value,
                        USER_STRING_REDUNDANCY)) + 1);
            }
            if (Const.SESSION_KEY_MOBILE.equals(key)) {
                user.setMobile(trim(value, USER_STRING_REDUNDANCY));
            }
        });
    }
}

上述代码中常量的定义:

public static final String SESSION_KEY_UID = ""uid"";
public static final String SESSION_KEY_USERNAME = ""un"";
public static final String SESSION_KEY_AVATAR = ""avt"";
public static final String SESSION_KEY_CST = ""cst"";
public static final String SESSION_KEY_MOBILE = ""mbl"";

public static final String USER_STRING_REDUNDANCY = """;

php写入的数据是uid、un、avt、cst、mbl这样的key,经过Msgpack序列化后的key都加上了",强制转换为Map对象后,map中的key并没有去掉这些转义字符,value也不例外,因此使用trim去掉了value两边的"。需要注意,如果是yii2的话,一般key都有前缀,此处作者把前缀删掉了,下面所有代码均如此。

此处的trim是经过封装的,和php中trim api类似。

上面的这块代码,Msgpack反序列化可以抽象封装为工具类:

@SuppressWarnings("unchecked")
public static Map getMessagePackValue(final byte[] bytes) throws IOException {
    MessagePack pack = new MessagePack();
    Map map = (Map) pack.read(bytes);
    Map result = new HashMap<>();
    if (null != map) {
        map.forEach((k, v) -> result.put(trim(String.valueOf(k), USER_STRING_REDUNDANCY),
                trim(String.valueOf(v), USER_STRING_REDUNDANCY)));
    }
    return result;
}
优化getMessagePackValue方法,不再使用trim处理数据
@SuppressWarnings("unchecked")
public static Map getMessagePackValue(final byte[] bytes) throws IOException {
    MessagePack pack = new MessagePack();
    Map map = (Map) pack.read(bytes, Templates.tMap(Templates.TString,
            Templates.TValue));

    Map result = new HashMap<>();
    if (map != null) {
        map.forEach((k, v) -> {
            Object object = rawObjToObj(v);
            result.put(k, object);
        });
    }
    return result;
}

Msgpack有很多数据模板,这里使用TString模板和TValue模板。相关模板均在org.msgpack.template.Template类中定义,诸如:TValue、TByte、TShort、TInteger、TLong、TCharacter、TBigInteger、TBigDecimal、TFloat、TBoolean、TString、TByteArray、TByteBuffer、TDate、tNotNullable、tList、tMap、tCollection、tOrdinalEnum。

private static Object rawObjToObj(Object obj) {
    Object objResu = null;
    if (obj instanceof IntegerValue) {
        objResu = ((IntegerValue) obj).asIntegerValue();
    } else if (obj instanceof ArrayValue) {
        ArrayValue v = ((ArrayValue) obj).asArrayValue();
        List ret = new ArrayList<>(v.size());
        v.forEach(x -> ret.add(x));
        objResu = ret;
    } else if (obj instanceof BooleanValue) {
        objResu = ((BooleanValue) obj).asBooleanValue();
    } else if (obj instanceof FloatValue) {
        objResu = ((FloatValue) obj).asFloatValue();
    } else if (obj instanceof MapValue) {
        MapValue value = ((MapValue) obj).asMapValue();
        Map map = new HashMap<>(value.size());
        value.forEach((k, v) -> {
            map.put(k, rawObjToObj(v));
        });
        objResu = map;
    } else {
        objResu = ((RawValue) obj).asRawValue().getString();
    }
    return objResu;
}
优化后的使用方法
// appId 为php中的session id
public User getAdminUser(String appId) {
    byte[] sessionKey = getSessionKey(appId).getBytes();
            byte[] sessionAdmin;
            User user = new User();
            try (Jedis jedis = jedisPool.getResource()) {
                jedis.select(RedisDB.DB12);
                sessionAdmin = jedis.get(sessionKey);
                user.setExpire(jedis.ttl(sessionKey));
            }
    if (sessionAdmin != null) {
        Map map = new HashMap<>();
        try {
            map = Utils.getMessagePackValue(sessionAdmin);
        } catch (IOException e) {
            LOGGER.warn("parse session admin error: " + e.getMessage());
        }
        if (!isEmpty(map)) {
            if (map.containsKey("id")) {
                if (map.get("id") instanceof IntegerValue) {
                    user.setAid(((IntegerValue) map.get("id")).longValue());
                } else if(!isEmpty((String) map.get("id"))){
                    user.setAid(Long.parseLong((String) map.get("id")));
                } else {
                    user.setAid(0L);
                }
            }
            if (map.containsKey("un")) {
                user.setUsername((String) map.get("un"));
            }
            if (map.containsKey("rn")) {
                user.setRealname((String) map.get("rn"));
            }
        }
    }
    return user;
}
错误:org.msgpack.type.intValueImpl cannot be cast to java.lang.Long

先看看这段代码:

if (map.containsKey("id")) {
    if (map.get("id") instanceof IntegerValue) {
        user.setAid(((IntegerValue) map.get("id")).longValue());
    } else if(!isEmpty((String) map.get("id"))){
        user.setAid(Long.parseLong((String) map.get("id")));
    } else {
        user.setAid(0L);
    }
}

反序列化后获取id,因为User中id是Long类型,这里面id可能是String,也可能是Integer,因此需要注意。最开的代码是这样写的:

if (map.containsKey("id") && !isEmpty((String) map.get("id"))) {
    user.setAid(Long.parseLong((String) map.get("id")));
}

通过Long.parseLong解析id报错,错误:org.msgpack.type.intValueImpl cannot be cast to java.lang.Long,于是修改为下面这般,如果是IntegerValue的实例,则强制转换为Long类型,但是依然报错:

if (map.containsKey("id")) {
    if (map.get("id") instanceof IntegerValue) {
        user.setAid((Long) map.get("id"));
    } else if(!isEmpty((String) map.get("id"))){
        user.setAid(Long.parseLong((String) map.get("id")));
    } else {
        user.setAid(0L);
    }
}

最后强制类型转换IntegerValue,再获取longValue,即为上面函数中的那般才解决此错误。

参考资料

1.让java版msgpack支持Object类型

2.MessagePack

3.msgpack

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/66570.html

相关文章

  • Python之celery的简介与使用

    摘要:的简介是一个基于分布式消息传输的异步任务队列,它专注于实时处理,同时也支持任务调度。目前支持等作为消息代理,但适用于生产环境的只有和官方推荐。任务处理完后保存状态信息和结果,以供查询。 celery的简介   celery是一个基于分布式消息传输的异步任务队列,它专注于实时处理,同时也支持任务调度。它的执行单元为任务(task),利用多线程,如Eventlet,gevent等,它们能被...

    LeexMuller 评论0 收藏0
  • MessagePack 编解码

    摘要:是一个高效的二进制序列化框架它像一样支持不同语言间的数据交换但是它的性能更快序列化之后的码流更小的特点如下编解码高效性能高序列化之后的码流小支持跨语言编码器和解码器开发编码器开发负责将类型的对象编码为数组然后添加到集合中解码器开发首先从数 MessagePack 是一个高效的二进制序列化框架, 它像 JSON 一样支持不同语言间的数据交换, 但是它的性能更快, 序列化之后的码流更小. ...

    xuxueli 评论0 收藏0
  • PHP四种序列化方案

    摘要:这里序列化的概念可能更为广泛和笼统一些,包括传统的等。就目前来看,等二进制序列化无论是速度上还是数据大小上,都要比文本序列化更好。,反序列化方法。用法,序列化方法。 原文地址:https://t.ti-node.com/thread/... 数据的序列化是一个非常有用的功能,然而目测很多人跟我一样,在刚接触这玩意的时候压根就不理解这货色到底是干啥用的,反正老师说了,实在理解不了就先背...

    Wildcard 评论0 收藏0
  • hudson+gradle+git+maven(非必选)持续集成 (二)

    摘要:第二集非必选持续集成编译打包部署到线上环境集成前言在持续集成第一集中讲解了关于如果编译,打包,发布包到私服。在下面一集中,也就是第二集中,开始讲解如何持续集成可运行的服务包到测试和正式环境。 第二集 hudson+gradle+git+maven(非必选)持续集成编译,打包,部署到线上环境集成 前言 在持续集成第一集中,讲解了关于如果编译,打包,发布jar包到maven私服。在下面一集...

    william 评论0 收藏0
  • Python爬虫之使用celery加速爬虫

    摘要:是一个基于分布式消息传输的异步任务队列,它专注于实时处理,同时也支持任务调度。本文将介绍如何使用来加速爬虫。本文爬虫的例子来自文章爬虫的种姿势。虽然没有这个爬虫框架和异步框架来的快,但这也可以作为一种爬虫的思路。   celery是一个基于分布式消息传输的异步任务队列,它专注于实时处理,同时也支持任务调度。关于celery的更多介绍及例子,笔者可以参考文章Python之celery的简...

    baihe 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<