MessagePack枚舉類的反序列化報錯:Caused by: javassist.CannotCompileException: [source error] no such constructor: com.example..


問題1:

反序列化的枚舉類:

@Message
public enum EnumClassd {
    Unknown   (0);
    @Getter
    @Setter
    private Integer code;
    EnumClassd(Integer code) {
        this.code = code;
    }

}

MessagePack反序列化上述枚舉類時報錯:

org.msgpack.MessageTypeException: org.msgpack.template.builder.TemplateBuildException: Cannot compile: 
{
  if (!$3 && $1.trySkipNil()) {
    return null;
  }
  com.example.EnumClass _$$_t;
  if ($2 == null) {
    _$$_t = new com.example.EnumClass();
  } else {
    _$$_t = (com.example.EnumClass) $2;
  }
  $1.readArrayBegin();
  if ($1.trySkipNil()) {  } else {
    org.msgpack.template.builder.DefaultBuildContext.readPrivateField($1, _$$_t, com.example.EnumClass.class, "code", templates[0]);
  }
  $1.readArrayEnd();
  return _$$_t;
}


    at org.msgpack.template.TemplateRegistry.buildAndRegister(TemplateRegistry.java:575)
    at org.msgpack.template.TemplateRegistry.lookupAfterBuilding(TemplateRegistry.java:472)
    at org.msgpack.template.TemplateRegistry.lookup(TemplateRegistry.java:251)
    at org.msgpack.template.builder.JavassistTemplateBuilder.toTemplate(JavassistTemplateBuilder.java:129)
    at org.msgpack.template.builder.JavassistTemplateBuilder.buildTemplate(JavassistTemplateBuilder.java:117)
    at org.msgpack.template.builder.AbstractTemplateBuilder.buildTemplate(AbstractTemplateBuilder.java:61)
    at org.msgpack.template.TemplateRegistry.buildAndRegister(TemplateRegistry.java:562)
    at org.msgpack.template.TemplateRegistry.lookupAfterBuilding(TemplateRegistry.java:472)
    at org.msgpack.template.TemplateRegistry.lookup(TemplateRegistry.java:251)
    at org.msgpack.template.TemplateRegistry.lookupGenericTypeImpl0(TemplateRegistry.java:320)
    at org.msgpack.template.TemplateRegistry.lookupGenericTypeImpl(TemplateRegistry.java:308)
    at org.msgpack.template.TemplateRegistry.lookupGenericType(TemplateRegistry.java:280)
    at org.msgpack.template.TemplateRegistry.lookup(TemplateRegistry.java:205)
    at org.msgpack.template.builder.JavassistTemplateBuilder.toTemplate(JavassistTemplateBuilder.java:129)
    at org.msgpack.template.builder.JavassistTemplateBuilder.buildTemplate(JavassistTemplateBuilder.java:117)
    at org.msgpack.template.builder.AbstractTemplateBuilder.buildTemplate(AbstractTemplateBuilder.java:61)
    at org.msgpack.template.TemplateRegistry.buildAndRegister(TemplateRegistry.java:562)
    at org.msgpack.template.TemplateRegistry.lookupAfterBuilding(TemplateRegistry.java:472)
    at org.msgpack.template.TemplateRegistry.lookup(TemplateRegistry.java:251)
    at org.msgpack.MessagePack.read(MessagePack.java:370)
    at com.example.jenkinscodehubtest.util.OsvfUtil.ReadNextBlock(OsvfUtil.java:61)
    at com.example.jenkinscodehubtest.JenkinsCodehubTestApplicationTests.deal(JenkinsCodehubTestApplicationTests.java:46)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.util.ArrayList.forEach(ArrayList.java:1249)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.util.ArrayList.forEach(ArrayList.java:1249)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: org.msgpack.template.builder.TemplateBuildException: Cannot compile: 
{
  if (!$3 && $1.trySkipNil()) {
    return null;
  }
  com.example.EnumClass _$$_t;
  if ($2 == null) {
    _$$_t = new com.example.EnumClass();
  } else {
    _$$_t = (com.example.EnumClass) $2;
  }
  $1.readArrayBegin();
  if ($1.trySkipNil()) {  } else {
    org.msgpack.template.builder.DefaultBuildContext.readPrivateField($1, _$$_t, com.example.EnumClass.class, "code", templates[0]);
  }
  $1.readArrayEnd();
  return _$$_t;
}

    at org.msgpack.template.builder.BuildContext.build(BuildContext.java:73)
    at org.msgpack.template.builder.DefaultBuildContext.buildTemplate(DefaultBuildContext.java:56)
    at org.msgpack.template.builder.JavassistTemplateBuilder.buildTemplate(JavassistTemplateBuilder.java:119)
    at org.msgpack.template.builder.AbstractTemplateBuilder.buildTemplate(AbstractTemplateBuilder.java:61)
    at org.msgpack.template.TemplateRegistry.buildAndRegister(TemplateRegistry.java:562)
    ... 86 more
Caused by: javassist.CannotCompileException: [source error] no such constructor: com.example.EnumClass
    at javassist.CtBehavior.setBody(CtBehavior.java:446)
    at javassist.CtBehavior.setBody(CtBehavior.java:412)
    at javassist.CtNewMethod.make(CtNewMethod.java:138)
    at org.msgpack.template.builder.BuildContext.buildReadMethod(BuildContext.java:144)
    at org.msgpack.template.builder.BuildContext.build(BuildContext.java:65)
    ... 90 more
Caused by: compile error: no such constructor: com.example.EnumClass
    at javassist.compiler.MemberCodeGen.atMethodCallCore2(MemberCodeGen.java:593)
    at javassist.compiler.MemberCodeGen.atMethodCallCore(MemberCodeGen.java:575)
    at javassist.compiler.MemberCodeGen.atNewExpr(MemberCodeGen.java:302)
    at javassist.compiler.ast.NewExpr.accept(NewExpr.java:73)
    at javassist.compiler.CodeGen.atAssignCore(CodeGen.java:860)
    at javassist.compiler.CodeGen.atVariableAssign(CodeGen.java:793)
    at javassist.compiler.CodeGen.atAssignExpr(CodeGen.java:747)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:332)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:351)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.CodeGen.atIfStmnt(CodeGen.java:391)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:355)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.CodeGen.atStmnt(CodeGen.java:351)
    at javassist.compiler.ast.Stmnt.accept(Stmnt.java:50)
    at javassist.compiler.CodeGen.atMethodBody(CodeGen.java:292)
    at javassist.compiler.Javac.compileBody(Javac.java:223)
    at javassist.CtBehavior.setBody(CtBehavior.java:438)
    ... 94 more

Disconnected from the target VM, address: '127.0.0.1:54789', transport: 'socket'

Process finished with exit code -1

解決方法:

將注解@Message改為注解@MessagePackOrdinalEnum即可解決(https://github.com/msgpack/msgpack/issues/66)

問題2:

Exception in thread "main" org.msgpack.MessageTypeException: Expected raw value, but got boolean
    at org.msgpack.unpacker.Accept.acceptBoolean(Accept.java:33)
    at org.msgpack.unpacker.MessagePackUnpacker.readOneWithoutStackLarge(MessagePackUnpacker.java:154)
    at org.msgpack.unpacker.MessagePackUnpacker.readOneWithoutStack(MessagePackUnpacker.java:139)
    at org.msgpack.unpacker.MessagePackUnpacker.readOne(MessagePackUnpacker.java:73)
    at org.msgpack.unpacker.MessagePackUnpacker.readString(MessagePackUnpacker.java:502)
    at org.msgpack.template.StringTemplate.read(StringTemplate.java:46)
    at org.msgpack.template.StringTemplate.read(StringTemplate.java:25)
    at org.msgpack.template.AbstractTemplate.read(AbstractTemplate.java:31)
    at com.my.msgpack.SimpleMessagePackPractice$MyMessage2_$$_Template_1305193908_1.read(SimpleMessagePackPractice$MyMessage2_$$_Template_1305193908_1.java)
    at org.msgpack.template.AbstractTemplate.read(AbstractTemplate.java:31)
    at org.msgpack.MessagePack.read(MessagePack.java:388)
    at org.msgpack.MessagePack.read(MessagePack.java:371)
    at com.my.msgpack.SimpleMessagePackPractice.main(SimpleMessagePackPractice.java:61)

 該報錯是因為MessagePack序列化的結果中只包含了value,而不包含key。因而在進行反序列化需要保證類中屬性的順序必須保證完全一致,否則就會出錯:

如果兩個屬性的類型一致,可以反序列化,但是值發生錯亂。
如果兩個屬性的類型不一致,會拋出類型不匹配異常。
因此解決方法是報錯序列化和反序列化的對象順序類型保持一致。
 
 
MessagePack詳情見下文:

一、MessagePack是什么

先看官方的定義:MessagePack是一種高效的二進制序列化格式。它允許您像JSON一樣在多個語言之間交換數據。但是,它更快並且更小。

 

從官方定義中,可以有如下的結論:

  1. MessagePack是一個二進制序列化格式,因而它序列化的結果可以在多個語言間進行數據的交換。

  2. 從性能上講,它要比json的序列化格式要好。

  3. 從結果大小上講,它要比json的序列化結果要小。

 

但是官方並沒有提MessagePack和google pb的對比,實際上從空間和時間兩個方面對比,pb均要優於MessagePack,但pb相對MessagePack 的缺點是支持的語言種類比較少,需要編寫專門的 .proto文件,使用上沒有MessagePack方便。

二、MessagePack的主要概念

 

2.1 type system

類型體系是MessagePack的基礎,也是MessagePack在序列化后比json占用空間小的關鍵。當前包含的type有如下幾類:
  • Integer represents an integer
  • Nil represents nil
  • Boolean represents true or false
  • Float represents a IEEE 754 double precision floating point number including NaN and Infinity
  • Raw
    • String extending Raw type represents a UTF-8 string
    • Binary extending Raw type represents a byte array
  • Array represents a sequence of objects
  • Map represents key-value pairs of objects
  • Extension represents a tuple of type information and a byte array where type information is an integer whose meaning is defined by applications or MessagePack specification
    • Timestamp represents an instantaneous point on the time-line in the world that is independent from time zones or calendars. Maximum precision is nanoseconds.
 
這個類型體系將我們在代碼開發中用到的數據格式進行了映射,並且通過Extension這個類型給使用者留出了自由擴充的空間,但由於表示形式的限制,當前Extension最多有127個。
 
每一種類型能夠表示的范圍可以查看MessagePack規范中的Limitation部分和Extension types部分。

2.2 formats

在MessagePack中一個value的組成格式是這樣的:類型[長度][data]。下面列出幾個示例,詳細完整的描述請看附錄中的MessagePack規范。
 

2.2.1常量型

比如對於null、true、false這三個值,在MessagePack會被固定的映射為如下的值。
format name
first byte (in binary)
first byte (in hex)
nil
11000000
0xc0
false
11000010
0xc2
true
11000011
0xc3

2.2.2 int型(包含有符號整數和無符號整數)

 
示例如下 
  • 0xcc表示當前的值的類型是無符號整數並且長度不超過8個bit,具體的值內容需要通過后續8個bit位的內容來計算
  • 0xcd表示當前的值的類型是無符號整數並且長度不超過16個bit,具體的值內容需要通過后續16個bit位的內容來計算
  • 0xd0表示當前的值的類型是有符號整數並且長度不超過8個bit,具體的值內容需要通過后續8個bit位的內容來計算
  • 0xd1表示當前的值的類型是有符號整數並且長度不超過16個bit,具體的值內容需要通過后續16個bit位的內容來計算
uint 8 stores a 8-bit unsigned integer  
+--------+--------+
|  0xcc  |ZZZZZZZZ|
+--------+--------+

uint 16 stores a 16-bit big-endian unsigned integer
+--------+--------+--------+
|  0xcd  |ZZZZZZZZ|ZZZZZZZZ|
+--------+--------+--------+

int 8 stores a 8-bit signed integer
+--------+--------+
|  0xd0  |ZZZZZZZZ|
+--------+--------+

int 16 stores a 16-bit big-endian signed integer
+--------+--------+--------+
|  0xd1  |ZZZZZZZZ|ZZZZZZZZ|
+--------+--------+--------+

2.2.3 字符串 

  • 0xd9表示當前的值的類型是字符串並且長度不超過(2^8)-1個bytes ,具體的長度需要通過后續8個bit位的內容來計算,字符串的具體內容是后續長度的byte所表示的內容
  • 0xda表示當前的值的類型是字符串並且長度不超過(2^16)-1個bytes ,具體的長度需要通過后續16個bit位的內容來計算,字符串的具體內容是后續長度的byte所表示的內容
str 8 stores a byte array whose length is upto (2^8)-1 bytes:
+--------+--------+========+
|  0xd9  |YYYYYYYY|  data  |
+--------+--------+========+

str 16 stores a byte array whose length is upto (2^16)-1 bytes:
+--------+--------+--------+========+
|  0xda  |ZZZZZZZZ|ZZZZZZZZ|  data  |
+--------+--------+--------+========+

2.2.4 數組 

  • 0xdc表示當前的值的類型是數組並且長度不超過(2^16)-1個元素 ,具體的長度需要通過后續16個bit位(兩個byte)的內容來計算,計算出來的值就是數組元素的個數
  • 0xdd表示當前的值的類型是數組並且長度不超過(2^32)-1個元素 ,具體的長度需要通過后續32個bit位(4個byte)的內容來計算,計算出來的值就是數組元素的個數
array 16 stores an array whose length is upto (2^16)-1 elements:
+--------+--------+--------+~~~~~~~~~~~~~~~~~+
|  0xdc  |YYYYYYYY|YYYYYYYY|    N objects    |
+--------+--------+--------+~~~~~~~~~~~~~~~~~+

array 32 stores an array whose length is upto (2^32)-1 elements:
+--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+
|  0xdd  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|    N objects    |
+--------+--------+--------+--------+--------+~~~~~~~~~~~~~~~~~+

2.2.5 小結

 

2.3 Serialization:type to format conversion

 
source types
output format
Integer
int format family (positive fixint, negative fixint, int 8/16/32/64 or uint 8/16/32/64)
Nil
nil
Boolean
bool format family (false or true)
Float
float format family (float 32/64)
String
str format family (fixstr or str 8/16/32)
Binary
bin format family (bin 8/16/32)
Array
array format family (fixarray or array 16/32)
Map
map format family (fixmap or map 16/32)
Extension
ext format family (fixext or ext 8/16/32)

 

If an object can be represented in multiple possible output formats, serializers SHOULD use the format which represents the data in the smallest number of bytes.

2.4 Deserialization: format to type conversion

source formats
output type
positive fixint, negative fixint, int 8/16/32/64 and uint 8/16/32/64
Integer
nil
Nil
false and true
Boolean
float 32/64
Float
fixstr and str 8/16/32
String
bin 8/16/32
Binary
fixarray and array 16/32
Array
fixmap map 16/32
Map
fixext and ext 8/16/32
Extension

 

三、為什么MessagePack比json序列化使用的字節流更少 

3.1 直觀對比

可以通過下圖的兩張圖簡單進行下對比,第一張圖是同一個數據類型的內容用json和messagepack序列化的結果。

 從第二張圖可以明顯看到messagepack要比json占用的空間更少。

3.2 序列化結果只有value且value進行了專屬映射

 這張圖是MessagePack官網上的,用來進行json和MessagePack序列化結果的對比,實際情況是否確實如此呢?

我本地使用的msgpack-0.6.12版本。
代碼如下:
public class MessagePackSerializationCompareJson {

    @Message // Annotation
    public static class MyMessage {
        // public fields are serialized.
        public boolean compact;
        public int schema;
        
        public  String toString() {
            return "compact:"+compact+";schema:"+schema;
        }

    }
    /**
     * 
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
      //初始化一個對象
        MyMessage src = new MyMessage();
        src.compact = true;
        src.schema=0;
        
      //利用MessagePack進行序列化
        MessagePack msgpack = new MessagePack();
        // Serialize
        byte[] bytes = msgpack.write(src);
        System.out.println("msgpack result length:"+bytes.length);
        
        //利用json進行序列化
        String jsonResult = JSONObject.toJSON(src).toString();
        System.out.println("json result length:"+jsonResult.getBytes().length);
        
    }

}

運行結果如下:

json result length:27
msgpack result length:3

json序列化的結果是27,和官網圖片中的結果相同。但MessagePack的序列化結果是3,要比官網中的數字小很多。
按照上面圖片的解釋應當是:
  • 第一個byte是82,表示序列化后的結果有兩個元素
  • 第二個byte是c3,表示第一個元素的值是true
  • 第三個byte是00,表示第二個元素的值是0
 
為了驗證我們的推測,我們可以在MyMessage類中再添加一個boolean類型的屬性,但不給這個屬性賦值,按照java的規范,這個屬性的值就是false,按照MessagePack的規范,就會被轉為一個byte的c2,這樣msgpack序列化后的長度值就是4.  而json序列化的增加值要增加不少,是屬性名稱的長度+5(false的長度)+4(要增加兩個雙引號,一個逗號,一個冒號),如果屬性名稱長度是4,則一共會增加13個byte,總長度就是40.
public class MessagePackSerializationCompareJson {

    @Message // Annotation
    public static class MyMessage {
        // public fields are serialized.
        public boolean compact;
        public int schema;
        public boolean link;
        
        
        public  String toString() {
            return "compact:"+compact+";schema:"+schema+";link:"+link;
        }

    }
    /**
     * 
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
      //初始化一個對象
        MyMessage src = new MyMessage();
        src.compact = true;
        src.schema=0;
        
      //利用json進行序列化
        String jsonResult = JSONObject.toJSON(src).toString();
        System.out.println("json result length:"+jsonResult.getBytes().length);
        
      //利用MessagePack進行序列化
        MessagePack msgpack = new MessagePack();
        // Serialize
        byte[] bytes = msgpack.write(src);
        System.out.println("msgpack result length:"+bytes.length);
        
    }

}

上面代碼的執行的結果也符合猜測:

json result length:40
msgpack result length:4

從這個數字上看,MessagePack明顯優於json,特別是在屬性多的情況下差距會更大。即使json中把key去掉,序列化后的結果也要比MessagePack占用的空間大。
 

3.2 序列化對象的屬性順序不能變動

3.1分析了MessagePack序列化的結果中只包含了value,而不包含key。因而在進行反序列化需要保證類中屬性的順序必須保證完全一致,否則就會出錯:
如果兩個屬性的類型一致,可以反序列化,但是值發生錯亂。
如果兩個屬性的類型不一致,會拋出類型不匹配異常。
 

3.2.1 順序不同,類型相同

public class SimpleMessagePackPractice {

    @Message // Annotation
    public static class MyMessage {
        // public fields are serialized.
        public boolean compact;
        public boolean link;

        public String toString() {
            return "link:" + link + ";compact:" + compact;
        }
    }

    @Message // Annotation
    public static class MyMessage2 {
        // public fields are serialized.
        public boolean link;
        public boolean compact;

        public String toString() {
            return "link:" + link + ";compact:" + compact;
        }
    }

    /**
     * 
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {

        //初始化一個對象
        MyMessage src = new MyMessage();
        src.compact = true;
        src.link = false;

        //利用MessagePack進行序列化
        MessagePack msgpack = new MessagePack();
        // Serialize
        byte[] bytes = msgpack.write(src);

        //利用MessagePack進行反序列化
        MyMessage2 dst = msgpack.read(bytes, MyMessage2.class);
        System.out.println("msgpack 原始數據:" + src);
        System.out.println("msgpack 反序列化:" + dst);
        
        
    }

}

上述代碼的執行結果如下:

msgpack 原始數據:link:false;compact:true
msgpack 反序列化:link:true;compact:false

3.2.1 順序不同,類型不同

public class SimpleMessagePackPractice {

    @Message // Annotation
    public static class MyMessage {
        // public fields are serialized.
        public boolean compact;
        public String link;

        public String toString() {
            return "link:" + link + ";compact:" + compact;
        }
    }

    @Message // Annotation
    public static class MyMessage2 {
        // public fields are serialized.
        public String link;
        public boolean compact;

        public String toString() {
            return "link:" + link + ";compact:" + compact;
        }
    }

    /**
     * 
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {

        //初始化一個對象
        MyMessage src = new MyMessage();
        src.compact = true;
        src.link = "www.baidu.com";

        //利用MessagePack進行序列化
        MessagePack msgpack = new MessagePack();
        // Serialize
        byte[] bytes = msgpack.write(src);

        //利用MessagePack進行反序列化
        MyMessage2 dst = msgpack.read(bytes, MyMessage2.class);
        System.out.println("msgpack 原始數據:" + src);
        System.out.println("msgpack 反序列化:" + dst);
        
        
    }

}

執行結果:

Exception in thread "main" org.msgpack.MessageTypeException: Expected raw value, but got boolean
    at org.msgpack.unpacker.Accept.acceptBoolean(Accept.java:33)
    at org.msgpack.unpacker.MessagePackUnpacker.readOneWithoutStackLarge(MessagePackUnpacker.java:154)
    at org.msgpack.unpacker.MessagePackUnpacker.readOneWithoutStack(MessagePackUnpacker.java:139)
    at org.msgpack.unpacker.MessagePackUnpacker.readOne(MessagePackUnpacker.java:73)
    at org.msgpack.unpacker.MessagePackUnpacker.readString(MessagePackUnpacker.java:502)
    at org.msgpack.template.StringTemplate.read(StringTemplate.java:46)
    at org.msgpack.template.StringTemplate.read(StringTemplate.java:25)
    at org.msgpack.template.AbstractTemplate.read(AbstractTemplate.java:31)
    at com.my.msgpack.SimpleMessagePackPractice$MyMessage2_$$_Template_1305193908_1.read(SimpleMessagePackPractice$MyMessage2_$$_Template_1305193908_1.java)
    at org.msgpack.template.AbstractTemplate.read(AbstractTemplate.java:31)
    at org.msgpack.MessagePack.read(MessagePack.java:388)
    at org.msgpack.MessagePack.read(MessagePack.java:371)
    at com.my.msgpack.SimpleMessagePackPractice.main(SimpleMessagePackPractice.java:61)

參考:https://www.i4k.xyz/article/dly41721/101995555

參考:https://github.com/msgpack/msgpack/blob/master/spec.md


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM