使用 @JsonAnySetter returns 映射 jackson 使用 javassist class 无法识别的字段
mapping jackson with @JsonAnySetter returns Unrecognized field with javassist class
我正在尝试在 tomcat 8.5 上使用 jersey 在休息 WS 中使用 jackson 将 json 字符串转换为对象。
该对象是在运行时使用 javassist(信息来自数据库)创建的,并添加了一个 "other" 用 @JsonAnySetter/Getter 注释的地图。
当我用 buildClass("MyClass") 调用 jackson 的映射器时,它抛出 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException
.
当启动时 MyClass 已加载到 class 路径中且未使用 buildClass 时,映射工作正常。
我猜是 Loader 有问题,但我不知道如何解决这个问题。
欢迎点评和反馈。
public class ClassFactory{
public Class<?> buildClass(String className){
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool);
CtClass cc = pool.makeClass(className);
ConstPool constPool = pool.get(className).getClassFile().getConstPool();
/* */
/* field creation loop */
/* */
// other map
CtField field = CtField.make("private java.util.Map other = new java.util.HashMap();", cc);
cc.addField(field);
// add other map getter
CtClass[] paramsAny = {pool.get(String.class.getName())};
cc.addMethod(CtNewMethod.make(pool.get(Object.class.getName()), "any", paramsAny,null, "{ return this.other.get();}", cc));
CtMethod m = cc.getDeclaredMethod("any", paramsAny);
// add @JsonAnyGetter to other map getter
AnnotationsAttribute annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag );
annotationAttribute.addAnnotation(new Annotation(JsonAnyGetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
// add other map setter
CtClass[] paramsSet = {pool.get(String.class.getName()), pool.get(Object.class.getName())};
cc.addMethod(CtNewMethod.make(pool.get("void"), "set", paramsSet,null, "{this.other.put(,);}", cc));
m = cc.getDeclaredMethod("set", paramsSet);
// add @JsonAnySetter to other map setter
annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag );
annotationAttribute.addAnnotation(new Annotation(JsonAnySetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
//build class
return cc.toClass(cl,null);
}
}
这是生成的一部分class
public class MyClass{
/* more fields */
private Map other = new HashMap();
@JsonAnyGetter
public Object any(String var1) {
return this.other.get(var1);
}
@JsonAnySetter
public void set(String var1, Object var2) {
this.other.put(var1, var2);
}
}
杰克逊映射器
ObjectReader reader = new ObjectMapper().reader();
Class<?> myClass = new ClassFactory().buildClass("MyClass");
Object myClassInstance =reader.forType(myClass).readValue(jsonString);
一些json
{
/* more fields */
"info1":"val1",
"info2" :"val2"
}
这是因为您缺少属性 "info1" 和 "info2" 的 setter 和 getter。
我用下面的代码测试了它,反序列化工作正常。请注意我将 setter 和 getter 添加到 class 定义的行。
public class ClassFactory {
public Class<?> buildClass(String className) throws NotFoundException, CannotCompileException {
Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
properties.put("info1", String.class);
properties.put("info2", String.class);
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool);
CtClass cc = pool.makeClass(className);
ConstPool constPool = pool.get(className).getClassFile().getConstPool();
CtField field = CtField.make("private java.util.Map other = new java.util.HashMap();", cc);
cc.addField(field);
// add other map getter
CtClass[] paramsAny = { pool.get(String.class.getName()) };
cc.addMethod(CtNewMethod.make(pool.get(Object.class.getName()), "any", paramsAny, null,
"{ return this.other.get();}", cc));
CtMethod m = cc.getDeclaredMethod("any", paramsAny);
// add @JsonAnyGetter to other map getter
AnnotationsAttribute annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
annotationAttribute.addAnnotation(new Annotation(JsonAnyGetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
// add other map setter
CtClass[] paramsSet = { pool.get(String.class.getName()), pool.get(Object.class.getName()) };
cc.addMethod(CtNewMethod.make(pool.get("void"), "set", paramsSet, null, "{this.other.put(,);}", cc));
m = cc.getDeclaredMethod("set", paramsSet);
// add @JsonAnySetter to other map setter
annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
annotationAttribute.addAnnotation(new Annotation(JsonAnySetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
for (Entry<String, Class<?>> entry : properties.entrySet()) {
cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));
// add getter
cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));
// add setter
cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));
}
return cc.toClass(cl, null);
}
private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
throws CannotCompileException {
String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
StringBuffer sb = new StringBuffer();
sb.append("public ").append(fieldClass.getName()).append(" ")
.append(getterName).append("(){").append("return this.")
.append(fieldName).append(";").append("}");
return CtMethod.make(sb.toString(), declaringClass);
}
private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
throws CannotCompileException {
String setterName = "set" + fieldName.substring(0, 1).toUpperCase()+ fieldName.substring(1);
StringBuffer sb = new StringBuffer();
sb.append("public void ").append(setterName).append("(")
.append(fieldClass.getName()).append(" ").append(fieldName)
.append(")").append("{").append("this.").append(fieldName)
.append("=").append(fieldName).append(";").append("}");
return CtMethod.make(sb.toString(), declaringClass);
}
private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
ClassPool pool = ClassPool.getDefault();
return pool.get(clazz.getName());
}
public class TestJson {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
String jsonString = "{ \"info1\":\"val1\", \"info2\" :\"val2\"}";
ObjectReader reader = new ObjectMapper().reader();
Class<?> myClass = new ClassFactory().buildClass("MyClass");
Object myClassInstance = reader.forType(myClass).readValue(jsonString);
}
Jackson
的 ClassIntrospector
无法识别来自 javaassist
class 加载程序的注释加载。从两个不同的 class 加载程序加载的相同 class 被认为是不同的。尝试将这些注释的加载 class 委托给父 class 加载器。
Loader cl = new Loader(pool);
cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnySetter");
cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnyGetter");
我正在尝试在 tomcat 8.5 上使用 jersey 在休息 WS 中使用 jackson 将 json 字符串转换为对象。
该对象是在运行时使用 javassist(信息来自数据库)创建的,并添加了一个 "other" 用 @JsonAnySetter/Getter 注释的地图。
当我用 buildClass("MyClass") 调用 jackson 的映射器时,它抛出 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException
.
当启动时 MyClass 已加载到 class 路径中且未使用 buildClass 时,映射工作正常。
我猜是 Loader 有问题,但我不知道如何解决这个问题。
欢迎点评和反馈。
public class ClassFactory{
public Class<?> buildClass(String className){
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool);
CtClass cc = pool.makeClass(className);
ConstPool constPool = pool.get(className).getClassFile().getConstPool();
/* */
/* field creation loop */
/* */
// other map
CtField field = CtField.make("private java.util.Map other = new java.util.HashMap();", cc);
cc.addField(field);
// add other map getter
CtClass[] paramsAny = {pool.get(String.class.getName())};
cc.addMethod(CtNewMethod.make(pool.get(Object.class.getName()), "any", paramsAny,null, "{ return this.other.get();}", cc));
CtMethod m = cc.getDeclaredMethod("any", paramsAny);
// add @JsonAnyGetter to other map getter
AnnotationsAttribute annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag );
annotationAttribute.addAnnotation(new Annotation(JsonAnyGetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
// add other map setter
CtClass[] paramsSet = {pool.get(String.class.getName()), pool.get(Object.class.getName())};
cc.addMethod(CtNewMethod.make(pool.get("void"), "set", paramsSet,null, "{this.other.put(,);}", cc));
m = cc.getDeclaredMethod("set", paramsSet);
// add @JsonAnySetter to other map setter
annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag );
annotationAttribute.addAnnotation(new Annotation(JsonAnySetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
//build class
return cc.toClass(cl,null);
}
}
这是生成的一部分class
public class MyClass{
/* more fields */
private Map other = new HashMap();
@JsonAnyGetter
public Object any(String var1) {
return this.other.get(var1);
}
@JsonAnySetter
public void set(String var1, Object var2) {
this.other.put(var1, var2);
}
}
杰克逊映射器
ObjectReader reader = new ObjectMapper().reader();
Class<?> myClass = new ClassFactory().buildClass("MyClass");
Object myClassInstance =reader.forType(myClass).readValue(jsonString);
一些json
{
/* more fields */
"info1":"val1",
"info2" :"val2"
}
这是因为您缺少属性 "info1" 和 "info2" 的 setter 和 getter。
我用下面的代码测试了它,反序列化工作正常。请注意我将 setter 和 getter 添加到 class 定义的行。
public class ClassFactory {
public Class<?> buildClass(String className) throws NotFoundException, CannotCompileException {
Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
properties.put("info1", String.class);
properties.put("info2", String.class);
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool);
CtClass cc = pool.makeClass(className);
ConstPool constPool = pool.get(className).getClassFile().getConstPool();
CtField field = CtField.make("private java.util.Map other = new java.util.HashMap();", cc);
cc.addField(field);
// add other map getter
CtClass[] paramsAny = { pool.get(String.class.getName()) };
cc.addMethod(CtNewMethod.make(pool.get(Object.class.getName()), "any", paramsAny, null,
"{ return this.other.get();}", cc));
CtMethod m = cc.getDeclaredMethod("any", paramsAny);
// add @JsonAnyGetter to other map getter
AnnotationsAttribute annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
annotationAttribute.addAnnotation(new Annotation(JsonAnyGetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
// add other map setter
CtClass[] paramsSet = { pool.get(String.class.getName()), pool.get(Object.class.getName()) };
cc.addMethod(CtNewMethod.make(pool.get("void"), "set", paramsSet, null, "{this.other.put(,);}", cc));
m = cc.getDeclaredMethod("set", paramsSet);
// add @JsonAnySetter to other map setter
annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
annotationAttribute.addAnnotation(new Annotation(JsonAnySetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);
for (Entry<String, Class<?>> entry : properties.entrySet()) {
cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));
// add getter
cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));
// add setter
cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));
}
return cc.toClass(cl, null);
}
private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
throws CannotCompileException {
String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
StringBuffer sb = new StringBuffer();
sb.append("public ").append(fieldClass.getName()).append(" ")
.append(getterName).append("(){").append("return this.")
.append(fieldName).append(";").append("}");
return CtMethod.make(sb.toString(), declaringClass);
}
private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
throws CannotCompileException {
String setterName = "set" + fieldName.substring(0, 1).toUpperCase()+ fieldName.substring(1);
StringBuffer sb = new StringBuffer();
sb.append("public void ").append(setterName).append("(")
.append(fieldClass.getName()).append(" ").append(fieldName)
.append(")").append("{").append("this.").append(fieldName)
.append("=").append(fieldName).append(";").append("}");
return CtMethod.make(sb.toString(), declaringClass);
}
private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
ClassPool pool = ClassPool.getDefault();
return pool.get(clazz.getName());
}
public class TestJson {
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
String jsonString = "{ \"info1\":\"val1\", \"info2\" :\"val2\"}";
ObjectReader reader = new ObjectMapper().reader();
Class<?> myClass = new ClassFactory().buildClass("MyClass");
Object myClassInstance = reader.forType(myClass).readValue(jsonString);
}
Jackson
的 ClassIntrospector
无法识别来自 javaassist
class 加载程序的注释加载。从两个不同的 class 加载程序加载的相同 class 被认为是不同的。尝试将这些注释的加载 class 委托给父 class 加载器。
Loader cl = new Loader(pool);
cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnySetter");
cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnyGetter");