misterchangray / magic-byte Goto Github PK
View Code? Open in Web Editor NEWa java tool for faster convertor byte2object
License: BSD 3-Clause "New" or "Revised" License
a java tool for faster convertor byte2object
License: BSD 3-Clause "New" or "Revised" License
有两点疑问:
1)dynamicSize 是标记动态计算的(总数-其他标记了长度=剩下的,就是它的数据), dynamicSizeOf 是标记从哪个成员上取长度的。它俩应该不冲突的。 我注释了检测,运行后的结果符合我的预期,但是不确定是否存在其他问题。
2)dynamicSize 必须带有 size 属性,然而size会自动裁剪。但是这个动态计算的长度是不固定的,我也不能确定size预设多少,只能将size标记足够大吗?对内存使用有影响吗?
这两个一起使用会出现 dynamicSize & dynamicSizeOf only use one in the class; 错误,不知道是框架有 bug?还是报错信息不准确,不能这么用?
有个上百个字段的结构体,一个个写order有点费劲
如果我的属性和内部类属性都有一个叫 length 的字段,在使用的时候就会导致序列化的时候只序列化了 length 字段,他标记的字段就不会被序列化
代码示例如下:
@MagicClass
class A {
@MagicField(order = 1)
private int length;
@MagicField(order = 2, dynamicSizeOf = "length")
private String value;
@MagicField(order = 3)
private int dataLength;
@MagicField(order = 4, dynamicSizeOf = "dataLength")
private List<B> data;
@MagicClass
public static class B {
@MagicField(order = 1)
private int length;
@MagicField(order = 2, dynamicSizeOf = "length")
private byte[] data;
}
}
这个在 2.4.0 之前的实现是不会出问题的,换成 2.4.0 之后才出的问题。导致我序列化的时候 length 序列化完了就直接跳到 dataLength 了(但是 length 的值是对的),没有序列化 value 的内容,当我把两个 length 属性的名字改成不一样的之后问题就解决了
增加对时间类:Date、Instant、LocalDate、LocalTime、LocalDateTime等的支持,默认按照 yyyy-MM-dd HH:mm:ss 格式及长度转换数据,或者根据指定的时间格式转换对应的值,也可以自己设定长度。同时除了这种方式还可以指定按照 int 或者 long 直接将时间转为秒或者毫秒的方式转换
枚举可以设置按照字段名或者下标值进行转换,下标值可以通过设置为 short、int还是long
应该有 custom 注解定义自定义长度解析之后就不检查有没有在注解里指定定长或者基于哪个属性的值确定长度了
存在使用对象的属性信息联合判断进行自定义序列化,或者需要已经序列化成二进制的数据做其他操作
自定义序列化里的 pack 和 unpack 方法有时候需要配合根基对象里的其他属性的值做序列化和反序列化
反序列化时通过消息类型直接映射到类,而不需要每次反序列化时指定类
由于协议比较老,部分地方使用的还是 yyyy-MM-dd HH:mm:ss 格式的字符串传的时间信息,而不是使用的秒数,所以希望可以以字符串的形式支持这种格式的时间和二进制之间的转换。也可以将这种转换提供为默认的实现了 MConverter 接口的自定义转换类的方式提供支持
在 CollectionReader.java 文件,readFormBuffer 方法中,
if(TypeEnum.LIST == this.fieldMetaInfo.getType()) {
long allocSize = count * fieldMetaInfo.getGenericsField().getElementBytes();
ExceptionUtil.throwIFOOM(allocSize, fieldMetaInfo.getGenericsField().getFullName());
//FIXME : 取消了count
//List<Object> list = new ArrayList<>(count);
List<Object> list = new ArrayList<>();
for(int i=0; i<count; i++) {
//FIXME 添加了if判断,尝试解决如下问题:
//FIXME 针对List<T> dySize = true, 并且T中含有dySizeOf的问题
//FIXME 针对 类下同时存在 ArrayList<Byte> dySizeOf 和 List<T> dySize = true, order=最后 的问题
//TODO 需要构建大量的测试类...
//TODO 需要测试 Size 过大 或者 过小问题
//目前简单测试无问题
//<1> 注释掉dySize 和 dySizeOf同时存在的检测
//<2>
//T中包含dySizeOf且没有计算,导致T.length长度偏小
//suffixBytes 好像永远是0 或者小于0?,故导致fillBytes偏大或者正常
//故fillBytes/T.length 计算出来的count偏大,但是buffer的读取有效数据是不变的
//故根据buffer是否存在有效数据,跳出循环; 否则在List中会返回很多空T
if(buffer.capacity() - buffer.position() <= 0) break;
list.add( fieldMetaInfo.getGenericsField().getReader().readFormBuffer(buffer, entity));
}
return list;
}
经过简单测试,序列化和反序列化 没有问题。因为没有全部翻阅内部处理流程, 就得作者从代码流程处理的角度考虑了。
原先只考虑过长度是另一个属性的情况,所以直接指定对应的 order 即可拿到长度,但是如果长度被封在了一个 bean 里,那么直接指定 order 是那个封装的对象,长度在封装的对象里里面,也就没法取了
目前的异常栈在报错的时候只会打印一个报错信息,如果是配置序列化没配对的情况则无法看到具体序列化或者反序列化到哪个类的哪个属性的时候出现的问题
既然新增的时候都是向后添加,1,2,3连续也不会产生问题,请问为何建议跳跃配置?
@MagicClass
public class Staff4 {
@magicfield(order = 1)
private int id;
@magicfield(order = 2, calcLength = true)
private int length;
@MagicConverter(converter = CustomBook3Converter.class, attachParams = "1")
@magicfield(order = 5, size = 2)
private List book; // 序列化器 不会触发
@MagicConverter(converter = CustomBook3Converter.class, attachParams = "2")
@magicfield(order = 7, size = 2)
private List book2; // 如果定义改为 private Book3 book2; 则序列化器会触发。
@magicfield(order = 14, size = 4)
private String name;
Hello Ray,
I have some doubt about the line 94 of the ClassParser
dynamicRef.setDynamicRef(fieldMetaInfo);
why do you set the dynamicRef to the fieldMetaInfo ?
class A {
/**
* size 必须设置,为了保险起见,默认给了100个数量,应该够了...
* 没有长度标识,只能动态计算,剩下的就是它
/
@magicfield(order = 4, size = 100, dynamicSize = true) //动态长度,从数据中获取...
protected List《B》 list;
}
class B{
/*
* 值长度,将值转化为byte[]后的值长度...即:value.size()
*/
@magicfield(order = 2)
private int len;
/**
* 真实数据
* 需要自行将数据格式转为byte..
*/
@MagicField(order = 3, dynamicSizeOf = 2)
private ArrayList<Byte> value;
}
这种情况下,只能将B改为Byte,然后每次都得分为多次进行编解码吗?或者写很多自定义序列化器?
因为 Class B 有很多种定义 。
对于一些不支持类型的序列化做一个自定义的拓展实现,这样可以增加灵活性
@magicfield(order = 6,size = 64)
private String devic
含有中文无法正常展示,咋办
看首页 readme 的示例,序列化枚举要配合自定义序列化实现,目前只能这样吗?
如何支持二维的数组呢 ,就是吧这样的二维的数据 怎么放入
@magicfield(order = 15, size = 4)
private List<List> param = List.of(
List.of(new UserInfo(), new UserInfo()),
List.of(new UserInfo(), new UserInfo()),
List.of(new UserInfo(), new UserInfo()),
List.of(new UserInfo(), new UserInfo())
);
@MagicClass(byteOrder = ByteOrder.LITTLE_ENDIAN)
@DaTa
public static class UserInfo {
@MagicField(order = 7)
private short name;
@MagicField(order = 9)
private byte age;
}
如何支持二维的数组呢 ,就是吧这样的二维的数据 怎么放入
[
[{"name":"t","age":1},{"name":"t","age":1}],
[{"name":"t","age":1},{"name":"t","age":1}],
[{"name":"t","age":1},{"name":"t","age":1}]
]
@MagicField(order = 1)
private int teacherLength;
@MagicField(order = 2, dynamicSizeOf = 1)
private Teacher[] teacher;
@MagicField(order = 3)
private int studentLength;
@MagicField(order = 4, dynamicSizeOf = 3)
private List<Student> studentList;
public static List<Classes> build(int count) {
List<Classes> classes = new ArrayList<>();
for (int i = 0; i <count; i++) {
Classes classes1 = new Classes();
classes1.setTeacher(Teacher.build(2).toArray(new Teacher[1]));
classes1.setStudentList(Student.build(2));
classes1.setStudentLength(0);
classes1.setTeacherLength(0);
classes.add(classes1);
}
return classes;
}
将 teacherLength
studentLength
强制赋值为 0,序列化结果
符合预期,但是序列化的过程中没有提示这里有不一致的情况或者抛出异常,不便于在实际使用时定位问题
@MagicClass
public class ParentClassA {
@MagicField(order = 1)
private int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}
@MagicClass
public class ChildClassA extends ParentClassA {
}
/**
* 测试constom converter 配置 dynamicsize 使用
*
* @throws ParseException
*/
@Test
public void testCustomConverterOfDynamicSize() throws ParseException {
ChildClassA classA = new ChildClassA();
classA.setNumber(1);
byte[] data = MagicByte.unpackToByte(classA);
Assert.assertEquals(4, data.length);
}
java.lang.AssertionError:
预期:4
实际:0
<点击以查看差异>
e.printStackTrace() 只能输出到控制台,没法通过日志门面向各个日志框架写到日志文件中。我感觉 lombok 可以不引入,slf4j 的的引入并不会造成太大的影响,而且 slf4j 属于日志门面,适配各个日志框架,灵活性要更高一些,引入之后也不会对项目造成影响。而且,一般做项目大多数都是搭配日志框架输出的,很少直接用控制台输出的,这样定位问题也不方便。
新增一个函数,可以返回定义的结构体占用字节数大小。
解决场景:
有时候定义完成后,字段很多。不方便一个一个手动的去数总字节数。
手动输入 cmd 容易输错了,还不好检查,不如强制实现 cmd 方法,然后注册只从 cmd 方法取值
直接在注解定义有些死板,是否可以考虑从其他属性读取这个参数值,传入实现的接口便于动态反序列化
根据我需要,我分数据头和数据体两部分,头的格式是固定的,数据体根据也不同结构也不一样,我能否直接通过仅在一个对象里封装头和数据体,来进行序列化和反序列化,就像下面代码的样子。或者别的方法来实现动态选择不同的数据体格式做序列化和反序列化?我不想先从二进制解出来一个头,然后拿着剩余的二进制数据再判断解析数据体,太麻烦了。
注:我知道 readme 写了不支持 Object,所以我下面的代码里给 Object 加个自定义转换器的注解,能否实现动态序列化转换?
@MagicClass(byteOrder = ByteOrder.BIG_ENDIAN)
public class School {
// 一个对象
@MagicField(order = 1)
private Header header;
@MagicField(order = 3)
@MagicConverter()
private Object age;
}
测试代码参考合并请求 #56
@MagicClass
public class CustomClassA {
@MagicField(order = 1)
private int number;
@MagicField(order = 2)
private B b;
@MagicClass
public static class B {
@MagicField(order = 1)
@MagicConverter(converter = TypeEnumConverter.class)
private TypeEnum typeEnum;
}
public enum TypeEnum {
A, B
}
目前通过调试追踪代码看到,在这种使用情况下,生成的属性 b 的 elementbytes 的值为 0,导致反序列化的时候跳过了对应属性的反序列化,导致反序列化结果里 b 的值为 null
如果在启动的时候像 spring 一样注解扫描,性能会比第一次使用的时候高,同时避免 cache 是 hashmap 的多线程问题
支持android调用
一般端序都是一个项目都用同一个,没必要在 @MagicClass(byteOrder = ByteOrder.BIG_ENDIAN)
中挨个设置,可以全局统一设置默认端序,如果有单独需要可以在注解里改
如果类中的部分属性不想参加序列化,希望可以增加像 @JsonIgnore 这样的注解标记忽视序列化
dynamicSizof wiki 没更新,新的 dynamicSizof 的改动是破坏性的,旧代码都要进行修改 >_<
Head pack = MagicByte.pack(deviceUploadMessage, Head.class);
,然后 HeartbeatCmd pack1 = MagicByte.pack(deviceUploadMessage, HeartbeatCmd.class);
用同一个二进制数据从头开始解析,数据结果是不对的,因为需要偏移起始位置,但是又要先获得 Head 的全长再去偏移生成对象,比较繁琐feat: length,checkcode 属性扩展支持 uint,ubyte等字段
如果 @magicfield 注解的属性类型是接口类则计算的长度为 0,因为在计算长度的时候是通过类的属性计算的,而不是通过实际类型动态计算的
希望新增一个方法:返回读取的字节数和对象,这个可以满足需求,返回类型感觉直接用 MResult 就行
目前的自定义序列化是对 list 遍历,然后对 list 里每一个 item 单独做序列化处理的,但是我希望可以对整个 list 做序列化,也就是直接把 list 给我,我来对 list 做整体的处理
如果 dynamicSize 可以支持 Object,是不是就可以运行时自动计算类型为接口类的属性的长度了
由于要传输中文,整个系统默认 utf-8 编码,一个个设置太麻烦了,还容易写错,不便于维护,需要将默认值改为 utf-8 或者像 spring 一样在配置文件中配置默认值,这样统一维护
由于自定义序列化的时候可能会根据根节点的其他属性综合进行自定义序列化操作,因此希望能传入根节点,便于进行序列化
能否提供一个方法获取标记为 magicclass 的类的二进制数据长度。长度有两种,一种是格式长度,就是那种根据协议字段定义的 magicfiled 长度;一种是填充数据后的长度,主要是有字段是动态长度的,填充值之后才能知道总长度。
如:
当第0个字节是 0x01的时候,则第1 ~ 4个字节 需要解析为:对象中的姓名
当第0个字节是0x02的时候,则第1 ~ 3个字节需要解析为:对象中的地址
对于校验和和检查校验和的实现希望可以自定义且支持返回 byte[],因为我希望对除了校验和属性之外属性已经序列化的二进制数据计算校验和,比如按 sha256 计算,那返回的值类型就一定是 byte[] 不能是非数组类型
在实现自定义解析的时候有时候会需要已经解析的数据辅助进行判断然后决定如何做二进制转对象,不想在 pack 里再重新解析一下之前已经解析过的数据,这样效率太低了。如果能把已解析完的属性给进来就太好了
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.