杰克逊解串器委托给下一个适用的解串器

时间:2018-10-24 06:22:54

标签: java json spring-boot jackson json-deserialization

我有一个用于查询一些数据的外部服务。数据将采用以下两种格式之一(第一种是“传统”格式,但需要支持):

{
    "foo": "John Smith"
}

{
    "foo": {
        "name": "John Smith",
        "bar": "baz"
    }
}

我要映射到以下POJO:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Outer {

    private Foo foo;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Foo {

        String name;
        String bar;

    }

}

第二种格式的数据(foo是一个对象)应该像其他任何POJO一样反序列化,但是给定第一种格式的数据(foo是字符串),将其转换为实例的Foo中,我想致电new Foo(<foo>, null)。为此,我创建了一个自定义解串器(@JsonComponent意味着该解串器将在春季之前通过杰克逊ObjectMapper界面注册到全球通用Module):

@JsonComponent
public class FooDeserializer extends JsonDeserializer<Outer.Foo> {

    @Override
    public Outer.Foo deserialize(JsonParser parser, DeserializationContext context)
            throws IOException {
        JsonNode node = parser.getCodec().readTree(parser);
        if (node.isTextual()) {
            return new Foo(node.asText(), null);
        }
        return <delegate to next applicable deserializer>;
    }

}

我在弄清楚如何执行“委托给下一个适用的反序列化器”这一部分时遇到了麻烦,因为我尝试过的每个解决方案(例如parser.getCodec().treeToValue(node, Outer.Foo.class))都再次使用了相同的自定义反序列化器,从而导致无限递归。这有可能吗?

1 个答案:

答案 0 :(得分:1)

记入schummar答案:How do I call the default deserializer from a custom deserializer in Jackson。遵循以上答案,
1. @JsonComponent批注应从自定义序列化器中删除,因为我们需要使用默认序列化器构造自定义序列化器,@JsonComponent不支持此方法。
2.使用BeanDeserializerModifierSimpleModule注册ObjectMapperserialize,并使用由默认序列化程序构造的自定义序列化程序修改序列化程序。
3.在自定义序列化器的import java.io.IOException; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; import com.fasterxml.jackson.databind.module.SimpleModule; public class DelegateDeserializer { public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException { ObjectMapper mapper = new ObjectMapper(); SimpleModule simpleModule = new SimpleModule(); simpleModule.setDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { if (Outer.Foo.class.isAssignableFrom(beanDesc.getBeanClass())) { return new FooDeserializer(deserializer, beanDesc.getBeanClass()); } return deserializer; } }); mapper.registerModule(simpleModule); Outer outer1 = mapper.readValue(getType1Json(), Outer.class); Outer outer2 = mapper.readValue(getType2Json(), Outer.class); System.out.println("deserialize json with object structure:"); System.out.println(outer1.getFoo().getName()); System.out.println(outer1.getFoo().getBar()); System.out.println("deserialize json with string field only:"); System.out.println(outer2.getFoo().getName()); System.out.println(outer2.getFoo().getBar()); } private static String getType1Json() { return " { " + " \"foo\": { " + " \"name\": \"John Smith\", " + " \"bar\": \"baz\" " + " } " + "} "; } private static String getType2Json() { return " { " + " \"foo\": \"John Smith\" " + "} "; } } 方法中,处理特殊情况,并将序列化委托给默认情况下的默认序列化器(对于正常情况)。

以下代码演示了如何实现以上几点。

主班

import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.ResolvableDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import jackson.Outer.Foo;

public class FooDeserializer extends StdDeserializer<Outer.Foo> implements ResolvableDeserializer {

    private static final long serialVersionUID = 1L;
    private final JsonDeserializer<?> defaultDeserializer;

    public FooDeserializer(JsonDeserializer<?> defaultDeserializer, Class<?> clazz) {
        super(clazz);
        this.defaultDeserializer = defaultDeserializer;
    }

    @Override
    public Outer.Foo deserialize(JsonParser parser, DeserializationContext context) throws IOException {
        if (parser.getCurrentToken() == JsonToken.VALUE_STRING) {
            JsonNode node = parser.getCodec().readTree(parser);
            if (node.isTextual()) {
                return new Foo(node.asText(), null);
            }
        }

        return (Foo) defaultDeserializer.deserialize(parser, context);
    }

    @Override
    public void resolve(DeserializationContext ctxt) throws JsonMappingException {
        ((ResolvableDeserializer) defaultDeserializer).resolve(ctxt);
    }

}

FooDeserializer类

 public class Outer {
    private Foo foo;

    public Foo getFoo() {
        return foo;
    }

    public void setFoo(Foo foo) {
        this.foo = foo;
    }

    public static class Foo {
        private String bar;
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getBar() {
            return bar;
        }

        public void setBar(String bar) {
            this.bar = bar;
        }

        public Foo() {
        }

        public Foo(String name, String bar) {
            this.name = name;
            this.bar = bar;
        }
    }

}

外班

public class VideoRecorderFragment extends Fragment implements View.OnClickListener, ActivityCompat.OnRequestPermissionsResultCallback {
@Override
public void onClick(View view) {
    switch (view.getId()) {

        case R.id.imageButtonThumbnail:
            //Intent intent_gallery = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Video.Media.EXTERNAL_CONTENT_URI);
            //startActivityForResult(intent_gallery,REQUEST_TAKE_GALLERY_VIDEO);

            Intent intent_gallery = new Intent(Intent.ACTION_GET_CONTENT);
            intent_gallery.addCategory(Intent.CATEGORY_OPENABLE);
            intent_gallery.setType("video/*");
            startActivityForResult(Intent.createChooser(intent_gallery, "Select video"),REQUEST_TAKE_GALLERY_VIDEO);

            break;
    }
}