如何忽略空的json对象,比如“car”:{},在与jackson反序列化之后,这些对象会导致空pojo

velaa5lx  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(499)

我有一个rest服务,它使用来自Angular ui和其他rest客户机的json。数据是基于一个复杂的实体结构,这些实体存储在一个有50个表的数据库中。问题是可选的onetoone关系,因为angular将可选对象作为空定义发送,如 "car": {}, . spring数据存储库将它们保存为空条目,我得到了一个json响应,如 "car": {"id": 545234, "version": 0} 回来。我没有发现可以忽略空对象的jackson注解,只有空或null属性。
雇员实体具有以下形式:

@Entity
public class Employee {
  @Id 
  @GeneratedValue
  private Long id;

  @Version
  private Long version;

  private String name;

  @OneToOne(cascade = CascadeType.ALL)
  @JoinColumn(name = "car_id")
  @JsonManagedReference
  private Car car;

  .
  .   Desk, getters and setters
  . 
}

另一边是一个参考点

@Entity
public class Car{
  @Id
  @GeneratedValue
  private Long id;

  @Version
  private Long version;

  private String name;

  @OneToOne(fetch = FetchType.LAZY, mappedBy = "employee")
  @JsonBackReference
  private Employee employee;

  .
  .   getters and setters
  . 
}

例如,我将此作为后期操作发送到我的服务

{
  "name": "ACME",
      .
      .
      .
  "employee": {
    "name": "worker 1",
    "car": {},
    "desk": {
      floor: 3,
      number: 4,
      phone: 444
    }
      .
      .
      .
  },
  "addresses": [],
  "building": {},
      .
      .
      .
}

我得到了保存的数据作为回应

{
  "id": 34534,
  "version": 0,
  "name": "ACME",
      .
      .
      .
  "employee": {
    "id": 34535,
    "version":0,
    "name": "worker 1",
    "car": {"id": 34536, "version": 0},
    "desk": {
      "id": 34538,
      "version":0,
      "floor": 3,
      "number": 4,
      "phone": 444
    }
      .
      .
      .
  },
  "addresses": [],
  "building": {"id": 34539, "version": 0},
      .
      .
      .
}

正如在响应中看到的,我得到了一个id、一个版本、许多空值和空字符串的空表行,因为当我保存(持久化)主反序列化公司类时,其他实体也被保存,因为它们被注解为级联。
我发现了很多例子,比如donotincludeemptobjecttojackson,有一个具体的pojo和一个具体的反序列化程序,但每个实体都需要自己的反序列化程序。这将导致对当前实体和将来的新实体(仅可选实体)进行许多工作。
我试过以下,我写了一个 BeanDeserializerModifier 并尝试在标准beandeserializer上 Package 自己的反序列化程序:

SimpleModule module = new SimpleModule();
    module.setDeserializerModifier(new BeanDeserializerModifier() {
        @Override
        public List<BeanPropertyDefinition> updateProperties(DeserializationConfig config,
                                                             BeanDescription beanDesc,
                                                             List<BeanPropertyDefinition> propDefs) {
            logger.debug("update properties, beandesc: {}", beanDesc.getBeanClass().getSimpleName());
            return super.updateProperties(config, beanDesc, propDefs);
        }

        @Override
        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                                                      BeanDescription beanDesc,
                                                      JsonDeserializer<?> deserializer) {

            logger.debug("modify deserializer {}",beanDesc.getBeanClass().getSimpleName());
            // This fails:
           // return new DeserializationWrapper(deserializer, beanDesc);
            return deserializer; // This works, but it is the standard behavior
        }
    });

这是 Package 纸(和错误):

public class DeserializationWrapper extends JsonDeserializer<Object> {
private static final Logger logger = LoggerFactory.getLogger( DeserializationWrapper.class );

    private final JsonDeserializer<?> deserializer;
    private final BeanDescription beanDesc;

    public DeserializationWrapper(JsonDeserializer<?> deserializer, BeanDescription beanDesc) {
        this.deserializer = deserializer;
        this.beanDesc = beanDesc;
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        logger.debug("deserialize in wrapper {} ",beanDesc.getBeanClass().getSimpleName());
        final Object deserialized = deserializer.deserialize(p, ctxt);
        ObjectCodec codec = p.getCodec();
        JsonNode node = codec.readTree(p);

         // some logig that not work
         // here. The Idea is to detect with the json parser that the node is empty.
         // If it is empty I will return null here and not the deserialized pojo

        return deserialized;
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt, Object intoValue) throws IOException {
        logger.debug("deserializer - method 2");
        intoValue = deserializer.deserialize(p, ctxt);
        return intoValue;
    }

    @Override
    public boolean isCachable() {
        return deserializer.isCachable();
    }
  .
  .     I try to wrap the calls to the deserializer
  .

反序列化 Package 器不工作,在第一次调用后崩溃,出现异常 com.fasterxml.jackson.databind.exc.MismatchedInputException: No _valueDeserializer assigned at [Source: (PushbackInputStream); line: 2, column: 11] (through reference chain: ... Company["name"]) 我的问题是:有没有一种方法可以扩展正在工作的标准反序列化程序的行为,即反序列化程序在解析时检测到当前的jsonnode为空并返回null而不是空的类示例?也许我的想法是错误的,有一个完全不同的解决方案?
在Angular ui方面解决它是没有选择的。我们使用Jackson2.9.5。

50few1ms

50few1ms1#

使用 BeanDeserializerModifier 使用自定义反序列化程序是个好主意。您只需要改进反序列化程序实现。在您的示例中,问题在于这些行:

final Object deserialized = deserializer.deserialize(p, ctxt); //1.
ObjectCodec codec = p.getCodec(); //2.
JsonNode node = codec.readTree(p); //3.

线路 1. 阅读 JSON Object . 排队 2. 以及 3. 你想创建一个 JsonNode 但是空的 JSON Object 已经排成一行了 1. . 接下来的两行将尝试读取负载的其余部分 JsonNode . Jackson 默认使用 BeanDeserializer 反序列化正则表达式 POJO 班级。我们可以扩展这个类并提供自己的实现。在版本中
2.10.1 deserialise 方法如下所示:

@Override
public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
{
    // common case first
    if (p.isExpectedStartObjectToken()) {
        if (_vanillaProcessing) {
            return vanillaDeserialize(p, ctxt, p.nextToken());
        }
        // 23-Sep-2015, tatu: This is wrong at some many levels, but for now... it is
        //    what it is, including "expected behavior".
        p.nextToken();
        if (_objectIdReader != null) {
            return deserializeWithObjectId(p, ctxt);
        }
        return deserializeFromObject(p, ctxt);
    }
    return _deserializeOther(p, ctxt, p.getCurrentToken());
}

在大多数情况下,不需要特殊治疗 vanillaDeserialize 方法将被调用。我们来看看:

private final Object vanillaDeserialize(JsonParser p, DeserializationContext ctxt, JsonToken t) throws IOException {
    final Object bean = _valueInstantiator.createUsingDefault(ctxt);
    // [databind#631]: Assign current value, to be accessible by custom serializers
    p.setCurrentValue(bean);
    if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
        String propName = p.getCurrentName();
        do {
            p.nextToken();
            SettableBeanProperty prop = _beanProperties.find(propName);

            if (prop != null) { // normal case
                try {
                    prop.deserializeAndSet(p, ctxt, bean);
                } catch (Exception e) {
                    wrapAndThrow(e, bean, propName, ctxt);
                }
                continue;
            }
            handleUnknownVanilla(p, ctxt, bean, propName);
        } while ((propName = p.nextFieldName()) != null);
    }
    return bean;
}

正如你所看到的,它几乎做了我们想要的一切,除了它创建了新的对象,即使是空的 JSON Object . 它在创建新对象之后检查字段是否存在。一行太远了。不幸的是,这个方法是私有的,我们不能重写它。我们把它复制到我们班上,再修改一下:

class EmptyObjectIsNullBeanDeserializer extends BeanDeserializer {

    EmptyObjectIsNullBeanDeserializer(BeanDeserializerBase src) {
        super(src);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (_vanillaProcessing) {
            return vanillaDeserialize(p, ctxt);
        }

        return super.deserialize(p, ctxt);
    }

    private Object vanillaDeserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        p.nextToken();
        if (p.hasTokenId(JsonTokenId.ID_FIELD_NAME)) {
            final Object bean = _valueInstantiator.createUsingDefault(ctxt);
            // [databind#631]: Assign current value, to be accessible by custom serializers
            p.setCurrentValue(bean);
            String propName = p.getCurrentName();
            do {
                p.nextToken();
                SettableBeanProperty prop = _beanProperties.find(propName);

                if (prop != null) { // normal case
                    try {
                        prop.deserializeAndSet(p, ctxt, bean);
                    } catch (Exception e) {
                        wrapAndThrow(e, bean, propName, ctxt);
                    }
                    continue;
                }
                handleUnknownVanilla(p, ctxt, bean, propName);
            } while ((propName = p.nextFieldName()) != null);
            return bean;
        }
        return null;
    }
}

您可以按以下方式注册:

class EmptyObjectIsNullBeanDeserializerModifier extends BeanDeserializerModifier {
    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        if (beanDesc.getBeanClass() == Car.class) { //TODO: change this condition
            return new EmptyObjectIsNullBeanDeserializer((BeanDeserializerBase) deserializer);
        }
        return super.modifyDeserializer(config, beanDesc, deserializer);
    }
}

简单 POC :

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonTokenId;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.BeanDeserializer;
import com.fasterxml.jackson.databind.deser.BeanDeserializerBase;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.deser.SettableBeanProperty;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.Data;

import java.io.File;
import java.io.IOException;

public class EmptyObjectIsNullApp {

    public static void main(String[] args) throws IOException {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();

        SimpleModule emptyObjectIsNullModule = new SimpleModule();
        emptyObjectIsNullModule.setDeserializerModifier(new EmptyObjectIsNullBeanDeserializerModifier());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(emptyObjectIsNullModule);

        Wrapper wrapper = mapper.readValue(jsonFile, Wrapper.class);
        System.out.println(wrapper);
    }
}

@Data
class Wrapper {
    private Car car;
}

@Data
class Car {
    private int id;
}

对于“空对象” JSON 有效载荷:

{
  "car": {}
}

以上代码打印:

Wrapper(car=null)

为了 JSON 带某些字段的有效负载:

{
  "car": {
    "id": 1
  }
}

以上代码打印:

Wrapper(car=Car(id=1))

相关问题