如何配置Gson序列化一组JSR-303 ConstraintViolation对象?

时间:2014-02-21 05:52:20

标签: json hibernate gson bean-validation hibernate-validator

我似乎无法找到如何使用Gson序列化Hibernate的约束违规实现。

这是我到目前为止所尝试的内容。

方法1

MyPojo aPojo = new MyPojo();
Gson gson = new Gson();
Set<ConstraintViolation<MyPojo>> violations = validator.validate(aPojo);
System.out.println(gson.toJson(violations));

因此错误而失败:

java.lang.UnsupportedOperationException: 
Attempted to serialize java.lang.Class: com.bar.baz.MyPojo. 
Forgot to register a type adapter?
    at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:67)
    at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:61)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
    at com.google.gson.internal.bind.ObjectTypeAdapter.write(ObjectTypeAdapter.java:107)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
    at com.google.gson.Gson.toJson(Gson.java:593)
    at com.google.gson.Gson.toJson(Gson.java:572)
    at com.google.gson.Gson.toJson(Gson.java:527)
    at com.google.gson.Gson.toJson(Gson.java:507)

方法2

Gson gson = new Gson();
Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);

System.out.println(
    gson.toJson(violations,
                new TypeToken<ConstraintViolation<MyPojo>>() {}.getType())
);

因为没有序列化MyPojo的属性而失败:

输出:{}

方法3

我期待这种方法将序列化委托给我的自定义Serializer,但它仍然失败:

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(
        new TypeToken<ConstraintViolation<MyPojo>>() {}.getType(),
        new JsonSerializer<ConstraintViolation<MyPojo>>() {
            @Override
            public JsonElement serialize(ConstraintViolation<MyPojo> src, Type typeOfSrc, JsonSerializationContext context) {
                JsonObject result = new JsonObject();

                result.addProperty("aTestProperty", "A Test Value");

                return result;
            }

        });

Gson gson = builder.create();

Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);
System.out.println(gson.toJson(violations));

然而它失败并出现此错误:

java.lang.UnsupportedOperationException: 
Attempted to serialize java.lang.Class: 
com.bar.baz.MyPojo. 
Forgot to register a type adapter?
    at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:67)
    at com.google.gson.internal.bind.TypeAdapters$1.write(TypeAdapters.java:61)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.write(ReflectiveTypeAdapterFactory.java:89)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.write(ReflectiveTypeAdapterFactory.java:195)
    at com.google.gson.internal.bind.ObjectTypeAdapter.write(ObjectTypeAdapter.java:107)
    at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:68)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:96)
    at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:60)
    at com.google.gson.Gson.toJson(Gson.java:593)
    at com.google.gson.Gson.toJson(Gson.java:572)
    at com.google.gson.Gson.toJson(Gson.java:527)
    at com.google.gson.Gson.toJson(Gson.java:507)

方法4

查看错误消息,我可能会这样做:

GsonBuilder builder = new GsonBuilder();
        builder.registerTypeAdapter(
                new TypeToken<ConstraintViolation<MyPojo>>() {}.getType(),
                new JsonSerializer<ConstraintViolation<MyPojo>>() {
                    @Override
                    public JsonElement serialize(ConstraintViolation<MyPojo> src, Type typeOfSrc, JsonSerializationContext context) {
                        JsonObject result = new JsonObject();

                        result.addProperty("aTestProperty", "A Test Value");

                        return result;
                    }

                });

        builder.registerTypeAdapter(
                new TypeToken<MyPojo>() {}.getType(),
                new JsonSerializer<MyPojo>() {
                    @Override
                    public JsonElement serialize(MyPojo src, Type typeOfSrc, JsonSerializationContext context) {
                        JsonObject result = new JsonObject();

                        result.addProperty("anotherTestProperty", "Another Test Value");

                        return result;
                    }

                });

        Gson gson = builder.create();

        Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);
        System.out.println(gson.toJson(violations));

但它失败并出现类似的错误。

方法5:工作但丑陋

我设法完成的唯一工作就是为ConstraintViolation注册具有供应商类型(Hibernate)特定实现的串行器:

Set<ConstraintViolation<MyPojo>> violations = validator.validate(MyPojo);

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(
        new TypeToken<ConstraintViolationImpl>() {}.getType(),
        new JsonSerializer<ConstraintViolation<MyPojo>>() {
            @Override
            public JsonElement serialize(ConstraintViolation<MyPojo> src, Type typeOfSrc, JsonSerializationContext context) {
                JsonObject result = new JsonObject();

                result.addProperty("aTestProperty", "A Test Value");

                return result;
            }

        });

Gson gson = builder.create();
System.out.println(gson.toJson(violations));

有没有办法让这项工作不依赖于ConstraintViolation的具体实施(即org.hibernate.validator.internal.engine.ConstraintViolationImpl)?

1 个答案:

答案 0 :(得分:2)

似乎没有合理的方法来序列化javax.validation.ConstraintViolation个对象。事实上,甚至杰克逊在试图序列化集合时也犯了错误:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: fromIndex(0) > toIndex(-1) (through reference chain: java.util.HashSet[0]->org.hibernate.validator.internal.engine.ConstraintViolationImpl["propertyPath"]->org.hibernate.validator.internal.engine.path.PathImpl["pathWithoutLeafNode"]->org.hibernate.validator.internal.engine.path.PathImpl["pathWithoutLeafNode"]->org.hibernate.validator.internal.engine.path.PathImpl["pathWithoutLeafNode"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:232)

目前,我只是将错误集转换为我编写的一组自定义POJO,并将其序列化。

Custom ValidationError POJO:

public class ValidationError {

    private String className;
    private String propertyPath;
    private String errorMessage;

    public static Set<ValidationError> fromViolations(Set violations) {
        Set<ValidationError> errors = new HashSet<ValidationError>();

        for (Object o : violations) {
            ConstraintViolation v = (ConstraintViolation) o;

            ValidationError error = new ValidationError();
            error.setClassName(v.getRootBeanClass().getSimpleName());
            error.setErrorMessage(v.getMessage());
            error.setPropertyPath(v.getPropertyPath().toString());
            errors.add(error);
        }

        return errors;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getPropertyPath() {
        return propertyPath;
    }

    public void setPropertyPath(String propertyPath) {
        this.propertyPath = propertyPath;
    }

    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    @Override
    public String toString() {
        return "ValidationError{" +
                "className='" + className + '\'' +
                ", propertyPath='" + propertyPath + '\'' +
                ", errorMessage='" + errorMessage + '\'' +
                '}';
    }
}

样本用法:

Set<ConstraintViolation<MyBean>> violations = validator.validate(myBean);
Set<ValidationError> errors = ValidationError.fromViolations(violations);

GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
System.out.println(gson.toJson(errors));

更新

为了记录,值得一提的是XStream可以将一组约束违规序列化为魅力:

XStream xstream = new XStream(new JettisonMappedXmlDriver());
xstream.setMode(XStream.NO_REFERENCES);
System.out.println(xstream.toXML(violations));

然而,生成的对象图太冗长,无论如何都不适合用于生产。您可以看到示例输出here