сопоставление Джексона с @JsonAnySetter возвращает нераспознанное поле с классом javassist - PullRequest
2 голосов
/ 14 марта 2019

Я пытаюсь преобразовать строку json в объект, используя Джексона в остальных WS, используя Джерси на tomcat 8.5.

объект создается во время выполнения с использованием javassist (с информацией, поступающей из БД) и добавлением«другая» карта, аннотированная @ JsonAnySetter / Getter.

Когда я вызываю маппер Джексона с buildClass («MyClass»), он выдает com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.

Когда MyClass уже загружен вclasspath при запуске без использования 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($1);}", 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($1,$2);}", 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);
}
}

это часть сгенерированного класса

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"
}

Ответы [ 2 ]

0 голосов
/ 14 марта 2019

Jackson ClassIntrospector не распознает загрузку аннотации из загрузчика класса javaassist. Один и тот же класс загружается из двух разных загрузчиков классов, считающихся разными. Попробуйте делегировать класс загрузки загрузчику родительского класса для этих аннотаций.

    Loader cl = new Loader(pool);
    cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnySetter");
    cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnyGetter");
0 голосов
/ 14 марта 2019

Это потому, что вам не хватает сеттера и геттера для свойств "info1" и "info2".

Я проверил его с помощью приведенного ниже кода, и десериализация работает нормально.Обратите внимание на строки, где я добавляю сеттер и геттеры к определению класса.

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($1);}", 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($1,$2);}", 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);

    }

enter image description here

...