json-iterator / java

jsoniter (json-iterator) is fast and flexible JSON parser available in Java and Go
http://jsoniter.com/
MIT License
1.51k stars 518 forks source link

Class loading issues in DYNAMIC mode #227

Open patrushev opened 5 years ago

patrushev commented 5 years ago

We have defined a simple POJO interface (Address) and need to load its implementation class (AddressImpl) dynamically at run time via separate class loader. The class loaded correctly and proper implementation mapping registered at Jsoniter as "JsoniterSpi.registerTypeImplementation(Address.class, AddressImpl.class);". However, Jsoniter fails on both serialize and deserialize with the following javassist exception: "javassist.CannotCompileException: [source error] no such class: foo.bar.AddressImpl". Jsoniter configured in DYNAMIC mode, but we also checked STATIC, which does not work either.

Below is more detailed description of the use case and the error stack trace:

Class implClass = factory.loadImplementationClass(Address.class); // will load AddressImpl class

JsoniterSpi.registerTypeImplementation(Address.class, implClass); JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); JsonStream.setMode(EncodingMode.DYNAMIC_MODE);

Object address1 = implClass.newInstance(); String str = ...; Object address2 = (Address)JsonIterator.deserialize(str, Address.class);

com.jsoniter.spi.JsonException: failed to generate decoder for: com.jsoniter.spi.ClassInfo@51297528 with [], exception: javassist.CannotCompileException: [source error] no such class: foo.bar.AddressImpl public static java.lang.Object decode_(com.jsoniter.JsonIterator iter) throws java.io.IOException { java.lang.Object existingObj = com.jsoniter.CodegenAccess.resetExistingObject(iter); byte nextToken = com.jsoniter.CodegenAccess.readByte(iter); if (nextToken != '{') { if (nextToken == 'n') { com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); return null; } else { nextToken = com.jsoniter.CodegenAccess.nextToken(iter); if (nextToken == 'n') { com.jsoniter.CodegenAccess.skipFixedBytes(iter, 3); return null; } } // end of if null } // end of if { nextToken = com.jsoniter.CodegenAccess.readByte(iter); if (nextToken != '"') { if (nextToken == '}') { return (existingObj == null ? new foo.bar.AddressImpl() : (foo.bar.AddressImpl)existingObj); } else { nextToken = com.jsoniter.CodegenAccess.nextToken(iter); if (nextToken == '}') { return (existingObj == null ? new foo.bar.AddressImpl() : (foo.bar.AddressImpl)existingObj); } else { com.jsoniter.CodegenAccess.unreadByte(iter); } } // end of if end } else { com.jsoniter.CodegenAccess.unreadByte(iter); }// end of if not quote java.lang.String state = null; java.lang.Long zip = null; java.lang.String city = null; java.lang.String street = null; java.lang.String country = null; do { switch (com.jsoniter.CodegenAccess.readObjectFieldAsHash(iter)) { case -1517218351: country = (java.lang.String)iter.readString(); continue; case -1417514060: zip = (java.lang.Long)(iter.readNull() ? null : java.lang.Long.valueOf(iter.readLong())); continue; case 230981954: city = (java.lang.String)iter.readString(); continue; case 1986331710: street = (java.lang.String)iter.readString(); continue; case 2016490230: state = (java.lang.String)iter.readString(); continue; } iter.skip(); } while (com.jsoniter.CodegenAccess.nextTokenIsComma(iter)); foo.bar.AddressImpl obj = (existingObj == null ? new foo.bar.AddressImpl() : (foo.bar.AddressImpl)existingObj); obj.setState(state); obj.setZip(zip); obj.setCity(city); obj.setStreet(street); obj.setCountry(country); return obj; }

at com.jsoniter.Codegen.gen(Codegen.java:86)
at com.jsoniter.Codegen.getDecoder(Codegen.java:25)
at com.jsoniter.CodegenImplNative.genReadOp(CodegenImplNative.java:213)
at com.jsoniter.CodegenImplNative.genField(CodegenImplNative.java:191)
at com.jsoniter.CodegenImplObjectHash.appendBindingSet(CodegenImplObjectHash.java:125)
at com.jsoniter.CodegenImplObjectHash.genObjectUsingHash(CodegenImplObjectHash.java:92)
at com.jsoniter.Codegen.genSource(Codegen.java:243)
at com.jsoniter.Codegen.gen(Codegen.java:68)
at com.jsoniter.Codegen.getDecoder(Codegen.java:25)
at com.jsoniter.JsonIterator.read(JsonIterator.java:385)
at com.jsoniter.JsonIterator.read(JsonIterator.java:375)
... 36 more

Caused by: javassist.CannotCompileException: [source error] no such class: foo.bar.AddressImpl at javassist.CtNewMethod.make(CtNewMethod.java:84) at javassist.CtNewMethod.make(CtNewMethod.java:50) at com.jsoniter.DynamicCodegen.gen(DynamicCodegen.java:18) at com.jsoniter.Codegen.gen(Codegen.java:78) ... 51 more Caused by: compile error: no such class: foo.bar.AddressImpl at javassist.compiler.MemberResolver.searchImports(MemberResolver.java:479) at javassist.compiler.MemberResolver.lookupClass(MemberResolver.java:422) at javassist.compiler.MemberResolver.lookupClassByName(MemberResolver.java:325) at javassist.compiler.TypeChecker.atNewExpr(TypeChecker.java:168) at javassist.compiler.ast.NewExpr.accept(NewExpr.java:75) at javassist.compiler.TypeChecker.atCondExpr(TypeChecker.java:310) at javassist.compiler.ast.CondExpr.accept(CondExpr.java:48) at javassist.compiler.CodeGen.doTypeCheck(CodeGen.java:266) at javassist.compiler.CodeGen.compileExpr(CodeGen.java:253) at javassist.compiler.CodeGen.atReturnStmnt2(CodeGen.java:641) at javassist.compiler.JvstCodeGen.atReturnStmnt(JvstCodeGen.java:443) at javassist.compiler.CodeGen.atStmnt(CodeGen.java:393) at javassist.compiler.ast.Stmnt.accept(Stmnt.java:53) at javassist.compiler.CodeGen.atStmnt(CodeGen.java:381) at javassist.compiler.ast.Stmnt.accept(Stmnt.java:53) at javassist.compiler.CodeGen.atIfStmnt(CodeGen.java:428) at javassist.compiler.CodeGen.atStmnt(CodeGen.java:385) at javassist.compiler.ast.Stmnt.accept(Stmnt.java:53) at javassist.compiler.CodeGen.atStmnt(CodeGen.java:381) at javassist.compiler.ast.Stmnt.accept(Stmnt.java:53) at javassist.compiler.CodeGen.atIfStmnt(CodeGen.java:428) at javassist.compiler.CodeGen.atStmnt(CodeGen.java:385) at javassist.compiler.ast.Stmnt.accept(Stmnt.java:53) at javassist.compiler.CodeGen.atStmnt(CodeGen.java:381) at javassist.compiler.ast.Stmnt.accept(Stmnt.java:53) at javassist.compiler.CodeGen.atMethodBody(CodeGen.java:321) at javassist.compiler.CodeGen.atMethodDecl(CodeGen.java:303) at javassist.compiler.ast.MethodDecl.accept(MethodDecl.java:47) at javassist.compiler.Javac.compileMethod(Javac.java:175) at javassist.compiler.Javac.compile(Javac.java:102) at javassist.CtNewMethod.make(CtNewMethod.java:79) ... 54 more

patrushev commented 5 years ago

We have tried setting context class loader of course with no apparent success.

In our view, Jsoniter should either accept parent class loader explicitly (as part of the configuration or as a parameter for an operation) or it should use some smarter class loading techniques (for instance similarly to org.mapstruct.factory.Mappers#getMapper(java.lang.Class)). Classes not always loaded by the system class loaders.

Jackson works fine in the above use case. No class loading issues observed so far. Although we found Jsoniter to be noticeably faster than Jackson for our use cases, we have to stick to Jackson until this issue gets resolved somehow.