Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

MisterChangRay/magic-byte

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

402 Commits

Repository files navigation

魔法字节(magic-byte)

GitHub (pre-)release GitHub issues GitHub closed issues GitHub

中文|English

1. 简介

在当代物联网行业中,由于隐私和安全问题,很多的公司选择使用自定义的私有二进制协议。 在C语言中,由于有结构体的加持,对象和字节数组转换起来就特别简单;但在java中,在没有原生支持的情况下,开发人员就只能够靠码力去读取解析数据然后转译成为对象 ,流程如下图:

在这看似简单的编码/解码过程中其实会伴随很多人头疼的问题,例如:

  • 大小端/网络字节序的处理
  • 无符号数/有符号数的处理
  • 多字节整数转换处理
  • ASCII码与字节之间的转换处理
  • 空指针/填充数据的处理
  • 数组对象/嵌套对象的处理

此项目的目的便是尽可能的解决上述问题,让大家将更多的时间聚焦在业务中。 在引入MagicByte后,只需要在类定义的同时使用注解声明字段的数据类型。 接下来就只需要调用两个简单的方法即可进行序列化:用于对象转字节的MagicByte.unpack();和用于字节转对象的MagicByte.pack()。 是不是很简单?马上试试吧!

2. 快速入门:

  1. 引入Jar包;
  2. @MagicClass对当前类进行全局配置
  3. @MagicField对需要转换的JAVA对象属性进行标注,支持对象组合嵌套,注意:不支持继承
  4. 使用MagicByte.pack()MagicByte.unpack()对数据或对象进行快速的序列化或反序列化
  5. (可选)支持使用MagicByte.registerCMD注册消息到 MagicByte, 前往查看示例
  6. (可选)支持使用@MagicConverter()注解来实现自定义序列化;前往查看枚举类自定义序列化示例

Maven项目可直接导入: 点击查看版本列表

<dependency>
 <groupId>io.github.misterchangray</groupId>
 <artifactId>magic-byte</artifactId>
 <version>2.4.4</version>
</dependency>

3. 代码示例

以下为简单的框架功能展示,实际项目中数据实体类定义建议参考 数据实体定义的最佳实践

下面的报文示例中, 共有 Student 和 School 两个数据报文:

// declare class must use public
// 使用大端模式, 默认为大端
@MagicClass(byteOrder = ByteOrder.BIG_ENDIAN)
public class School {
 // 10 byte, 普通数据类型, 占用10字节长度
 @MagicField(order = 1, size = 10)
 private String name;
 // 2 byte, 长度字段, 数据序列化时将自动填充实际数据长度
 @MagicField(order = 3, calcLength = true)
 private short length;
 // 支持组合模式, 这里嵌入了 Student 对象
 // 总字节数 = students.bytes * length
 @MagicField(order = 5, size = 2)
 private Student[] students;
 // 0 byte, 注意, 此处无法序列化, 不支持的数据类型将会被忽略
 @MagicField(order = 7)
 private List<Object> notSupport;
 // 0 byte, 注意, 此处无法序列化, 不支持的数据类型将会被忽略
 @MagicField(order = 9)
 private Object age;
 // 4 byte, 注意, 此处指定为秒级时间戳, 同时指定使用4个字节保存, 未指定则默认6个字节
 @MagicField(order = 13, size = 4, timestampFormat = TimestampFormatter.TO_TIMESTAMP_SECONDS)
 private Date[] birthdays;
 // 1 byte, 普通数据类型, 通过order配置序列化顺序, 序列号顺序和定义顺序无关
 @MagicField(order = 15)
 private byte age;
 // 1 byte, 校验和字段, 序列化时如提供计算函数则将会自动填充
 @MagicField(order = 17)
 private byte checkCode;
 
 // getter and setter ...
}
@MagicClass()
public class Student {
 // 10 byte, 普通数据, 长度为 10 字节
 @MagicField(order = 1, size = 10)
 private String name;
 // 4 byte, 普通数据, 整数, 此字段决定后续 phones 字段长度
 @MagicField(order = 5)
 private int length;
 // 总字节数 = phones.size * length
 // 单个元素 8 byte, 此List并未直接指定大小, 大小由 length 字段决定. length字段数据类型只能为 byte, short, int, UNumber
 @MagicField(order = 10, dynamicSizeOf = "length")
 private List<Long> phones;
 // 1 byte
 @MagicField(order = 15)
 private byte age;
 // 生日, 这里为秒级时间戳, 指定使用4个字节, 日期类型未指定则默认6个字节
 @MagicField(order = 18, size = 4, timestampFormat = TimestampFormatter.TO_TIMESTAMP_SECONDS)
 private Date birthDay;
 // getter and setter ...
}
public class Hello {
 void main() {
 // 全局配置校验和计算函数
 MagicByte.configMagicChecker(Checker::customChecker);
 School school = new School();
 school.setAge((byte) 23);
 
 // you can set other propertis
 // object to bytes
 // 也可以单独传入计算函数
 byte[] bytes = MagicByte.unpack(school, Checker::customChecker); 
 School school2 = MagicByte.pack(bytes, School.class); // bytes to object
 System.out.println(school.getAge() == school2.getAge()); // out put true
 
 }
}
public class Checker {
 /**
 * 序列化时: data数据中包含所有已序列化的数据(包括 calcLength 也已经调用并序列化)
 * 反序列化时: data数据为传入数据的副本
 * @param data
 * @return
 */
 public static byte[] customChecker(byte[] data) {
 return new byte[]{0xff};
 }
}

4. 注解和属性说明

工具存在三个注解:

  1. @MagicClass() 类注解; 主要用于数据全局配置
    • byteOrder 配置序列化大小端,可全局配置
    • strict 严格模式, 默认false, 严格模式将会抛出更多的异常
  2. @MagicField() 属性注解, 未注解的属性不参与序列化/反序列化过程
    • order 定义对象属性的序列化顺序(重要, 投入使用后请勿修改, 从1开始递增,建议跳跃配置如:1,3,5...)
    • size 属性大小, 仅UNumber&String&List需要设置, String/UNumber 代表字节长度. List和Array代表成员个数
    • cmdField 标记此字段为消息类型, 此配置结合消息注册使用. 默认false; 消息注册参考
    • charset 字符集设置,可全局配置, 仅String设置有效; 默认ASCII
    • dynamicSize 标记字段为动态长度, 整条消息只能标记一次且仅能标记String&List&Array类型字段; 点击查看详情
    • dynamicSizeOf 从指定的属性中获取List或Array的长度, 仅List,Array,String有效;引用字段类型只能为byte, short, int, UNumber
    • calcLength 标记字段为长度字段, 反序列化时将自动将数据总长度(字节数)填充到此字段; 可能抛出: InvalidLengthException
    • calcCheckCode 标记字段为校验和字段, 序列化或反序列化时将会校验或自动填充; 可能抛出: InvalidCheckCodeException
    • timestampFormat 可指定时间格式,时间戳或者文本,时间戳可指定为毫秒,秒,分钟,小时,天;日期类型默认6字节储存空间,可使用size进行调整;如秒级时间戳4个字节就足够储存传输
    • formatPattern 日期字段使用,如指定序列化为字符串,这里配置序列化格式。默认为:yyyyMMddHHmmss
  3. @MagicConverter()配置自定义序列化,更多说明参考 自定义序列化最佳实践
    • converter, 序列化类, 该类必须为MConverter的子类
    • attachParams, 附加参数;序列化时将会传入
    • fixSize, 固定数据字节长度, 可以统一指定自定义数据的长度,也可忽略然后在序列化时返回实际数据长度

5. 支持的数据类型及字节大小;

数据类型 数据类型 扩展类型(无符号数) 字节大小
byte boolean UByte 1
short char UShort 2
int float UInt 4
long double ULong 8
Date,Instant,DateTime LocalTime,LocalDate,LocalDateTime 6(配合size修改)
String UNumber custom(配合size指定)

6. 注意事项

  1. 因为order属性为对象序列化顺序,所以已投入使用的字段请不要随意修改order属性(重要),这样可能会影响已有业务;新增字段请递增使用新的order
  2. 大端小端使用@MagicClass进行配置
  3. 基本数据类型使用下表默认字节长度, String/List/Array/UNumber 需要使用size属性指定成员长度或字符串字节长度
  4. 请使用基础类型定义报文结构,目前仅支持以下数据类型:
    1. 四类八种基础类型(byte/char/short/int/long/float/double/boolean)
    2. 无符号包装类型(UByte/UShort/Uint/ULong/UNumber) 关于无符号数类型
    3. 支持 String, 但必须申明 Size
    4. 支持 List & Array, 可以使用泛型; 仅支持一维数组且不能使用可变数据类型,如:List<String>String[]List<UNumber>
  5. 数据溢出时工具会自动对数据进行裁剪,如字符串或数组长度声明为5, 则只会序列化集合前5个元素
  6. 字符串默认使用ASCII编码,可局部配置或全局修改
  7. 不支持一维以上的List或者Array
  8. boolean值 0=false/非0=true
  9. 所有类的定义必须为 public, 不支持私有内部类; 支持 public static class XXX {}
  10. 不支持类继承的序列化和反序列化;支持类的嵌套组合使用
  11. 序列化null值,如果是包装数据类型,则使用原始类型默认值;如Short a = null; 序列化为 0; 其他数据类型将会直接填充,如数组,对象等。
  12. 更多问题请前往WIKI页面查看 >> WIKI_HOME
  13. 内置枚举类序列化转换器SimpleEnumConverter,原理是根据枚举定义顺序进行序列化/反序列化.故使用后请不要更改定义顺序。建议自行实现

7. 开发建议

  1. 不建议网络传输浮点数;
  2. 不建议网络传输有符号数即负数
  3. 不建议网络传输字符串
  4. 不建议类中大量声明字符串
  5. 条件允许建议使用protobuf作为序列化框架
  6. 二进制协议建议单包大小在100KB以内
  7. order属性建议跳跃配置(如1,3,5,7), 且对于已经投产使用的字段一定不要随意修改顺序; 新增字段时必须使用递增后的order新值; 重要的事强调3次

8. 最后

本人目前也是做物联网相关领域的(共享充电宝), 开始的时候也是走了不少弯路,这个项目算是在这工作了1年多的工作总结; 写完之后才发现谷歌早在13年前就开源了类似框架, 真是学习使人进步; 又重复造轮子了。 附上谷歌的链接:

9.欢迎大佬入驻交流:

QQ群 562371124

About

a java tool for faster convertor byte2object

Topics

Resources

License

Stars

Watchers

Forks

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /