对于mapper.readValue,后备类<! - ? - >

时间:2017-04-05 08:34:26

标签: java json jackson deserialization

我在我的应用程序中使用Jackson进行de / serialization。

我有一种情况需要将JSON字符串转换为我的3个类之一。如果字符串不能转换为3个类别中的任何一个,那么它将被认为是一个无法识别的情况。

但是,如果json字符串的架构与mapper.readValue(jsonString,MyClass1.class)中提供的类不匹配,则会抛出UnrecognizedPropertyException

目前我正在使用类似下面的内容,但它看起来非常混乱。

try {
    obj = mapper.readValue(jsonString, MyClass1.class);             
} catch (UnrecognizedPropertyException e1) {
    try {
        obj = mapper.readValue(jsonString, MyClass2.class);
    } catch (UnrecognizedPropertyException e2) {
        try {
            obj = mapper.readValue(jsonString, MyClass3.class);
        } catch (Exception e) {
            //handle unrecognized string
        }
    } catch (Exception e) {
        //handle unrecognized string
    }
} catch (Exception e) {
    //handle unrecognized string
}

这是它需要完成的方式还是有其他选择?是否有任何方法可以将mapper配置为在无法识别的属性的情况下返回null,因为这会导致创建简单的系列if块而不是嵌套的try-catch块?

3 个答案:

答案 0 :(得分:1)

您可以尝试使用此方法进行反序列化。这将在null上返回UnrecognizedPropertyException

private <T> T deserialize(ObjectMapper mapper, Class<T> type, String jsonString) {
        T t = null;
        try {
            t = mapper.readValue(jsonString, type);
        } catch (UnrecognizedPropertyException  e) {
            //handle unrecognized string
        }catch (IOException  e) {
            //handle under errors
        }
        return t;
    }

答案 1 :(得分:0)

如果您生成jsonString,则可以考虑添加type info,然后使用它来转换反序列化对象。您可以参考this post了解如何操作。

如果jsonString由您无法控制的其他服务生成,那么您无法获得类型信息,因此您只能逐个尝试,@ Sachin Gupta的答案将是一个不错的选择。

我想提供一个额外选项:定义一​​个all-in-one实体,其中包括MyClass1MyClass2MyClass3的所有字段,并生成MyClass1MyClass2MyClass3是分开的包装器,只为每个包装器公开相关的字段。代码如下:

班级AllInOne

public class AllInOne {
    protected String a;
    protected String b;
    protected String c;

    public A asA() {
        return new A(this);
    }

    public B asB() {
        return new B(this);
    }

    public C asC() {
        return new C(this);
    }
}

班级A

public class A {
    private AllInOne allInOne;

    public A(AllInOne allInOne) {
        this.allInOne = allInOne;
    }

    public String getA() {
        return allInOne.a;
    }
}

班级B

public class B {
    private AllInOne allInOne;

    public B(AllInOne allInOne) {
        this.allInOne = allInOne;
    }

    public String getB() {
        return allInOne.b;
    }
}

班级C

public class C {
    private AllInOne allInOne;

    public C(AllInOne allInOne) {
        this.allInOne = allInOne;
    }

    public String getC() {
        return allInOne.c;
    }
}

测试代码:

public class Main {
    public static void main(String[] args) throws IOException {
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

        String jsonA = "{\"a\":\"a value\"}";
        String jsonB = "{\"b\":\"b value\"}";
        String jsonC = "{\"c\":\"c value\"}";

        needTypeA(om.readValue(jsonA, AllInOne.class).asA());
        needTypeB(om.readValue(jsonB, AllInOne.class).asB());
        needTypeC(om.readValue(jsonC, AllInOne.class).asC());
    }

    private static void needTypeA(A a) {
        System.out.println(a.getA());
    }

    private static void needTypeB(B b) {
        System.out.println(b.getB());
    }

    private static void needTypeC(C c) {
        System.out.println(c.getC());
    }
}

通过这样的实现,我们在反序列化步骤中删除了特定的类型信息,并在我们真正需要/使用它时将其恢复。正如你所看到的,没有太多额外的代码,因为我们实际上只是将所有字段声明一起移动,并添加了几个方法。

注意:

  • 我将AllInOne中的字段声明为protected,将所有POJO类放在同一个包中会使ABC能够直接访问它们,但不能访问其他类。
  • 设置om.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);以使field进行杰克森反序列化,以便我们可以从setter
  • 删除重复的getterAllInOne
  • 如果您确实需要知道类型信息,可以根据字段信息在isA内添加AllInOne等方法

答案 2 :(得分:0)

如果json包含一些define属性,那么您可以尝试使用@JsonTypeInfo@JsonSubTypes。类MyClass1, ...必须实现此接口。另外,我不记得如何将未知实现映射到null。

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.EXISTING_PROPERTY, // level of define property
        property = <property_name>,
        visible = true,
        defaultImpl = NoClass.class)
@JsonSubTypes({@JsonSubTypes.Type(value = <interface-impl>.class, name = <property_value>)})
private <interface> value;
// getters and setters