当前位置: 首页 > 知识库问答 >
问题:

JsonDeserializer不对类起作用,而只对类的单个元素起作用

郭乐意
2023-03-14

我创建了一个新的反序列化器,以便能够将空字符串写入为null

public class CustomDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException  {
        JsonNode node = jsonParser.readValueAsTree();
        if (node.asText().isEmpty()) {
            return null;
        }
        return node.toString();
    }
}

尝试在每个用户字段上做一个注释,自定义工作,但通过在整个类上插入注释,我不能再打印Json消息

@JsonDeserialize(using = CustomDeserializer.class)
public class User {
    private String firstName;
    private String lastName;
    private String age;
    private String address; }

CustomExceptionHandler向我抛出以下错误:Class MethodArgumentNotValidException这是我的Kafka使用者,我唯一输入验证注释的使用者,但即使删除它也会出现同样的错误

public class KafkaConsumer {

    @Autowired
    private UserRepository userRepository;

    @KafkaListener(topics = "${spring.kafka.topic.name}")
    public void listen(@Validated User user) {

        User  user = new User(user);
        UserRepository.save(user.getName(), user);
    }
}

对象映射器

public ObjectMapper getObjectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.setSerializationInclusion(Include.NON_NULL);
    return mapper;
}

有没有可能让它在整个班级都起作用?

共有1个答案

慕和惬
2023-03-14

如果希望将表示整个对象的空字符串视为null,则可以启用accept_empty_string_as_null_objectJackson反序列化特性,默认情况下该特性是禁用的。

您可以在配置ObjectMapper时包含它:

public ObjectMapper getObjectMapper() {
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    mapper.setSerializationInclusion(Include.NON_NULL);
    // Enable ACCEPT_EMPTY_STRING_AS_NULL_OBJECT deserialization feature
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
    return mapper;
}

如上所述,当您希望将表示整个对象的空字符串视为null时,它非常有用;但是,它不适用于string类型的单个属性:在后一种情况下,您可以安全地使用自定义反序列化器,因此,解决方案实际上是两种方法的混合,使用accept_empty_string_as_null_object反序列化特性处理整个对象,使用自定义反序列化器处理单个string属性。

请看这个和这个其他相关的SO问题。

您还可以改进自定义的用户反序列化器。请考虑,例如(为了清晰起见,我将名称重构为userdeserializer):

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

public class UserDeserializer extends JsonDeserializer<User> {

  @Override
  public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
    JsonNode node = jsonParser.readValueAsTree();
    Iterator<String> fieldNames = node.fieldNames();
    // Process Jackson annotations looking for aliases
    Map<String, String> fieldAliases = this.getAliases();
    User user = new User();
    boolean anyNonNull = false;
    // Iterate over every field. The deserialization process assume simple properties
    while(fieldNames.hasNext()) {
      String fieldName = fieldNames.next();
      JsonNode fieldValue = node.get(fieldName);
      String fieldValueTextRepresentation = fieldValue.asText();
      if (fieldValueTextRepresentation != null && !fieldValueTextRepresentation.trim().isEmpty()) {
        // Check if the field is aliased
        String actualFieldName = fieldAliases.get(fieldName);
        if (actualFieldName == null) {
          actualFieldName = fieldName;
        }

        this.setFieldValue(user, actualFieldName, fieldValueTextRepresentation);
        anyNonNull = true;
      }
    }

    return anyNonNull ? user : null;
  }

  // Set field value via Reflection
  private void setFieldValue(User user, String fieldName, String fieldValueTextRepresentation) {
    try {
      Field field = User.class.getDeclaredField(fieldName);
      Object fieldValue = null;
      Class clazz = field.getType();
      // Handle each class type: probably this code can be improved, but it is extensible and adaptable,
      // you can include as many cases as you need.
      if (clazz.isAssignableFrom(String.class)) {
        fieldValue = fieldValueTextRepresentation;
      } else if (clazz.isAssignableFrom(LocalDate.class)) {
        // Adjust the date pattern as required
        // For example, if you are receiving the information
        // like this: year-month-day, as in the provided example,
        // you can use the following pattern
        fieldValue = LocalDate.parse(fieldValueTextRepresentation, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
      } else if (clazz.isAssignableFrom(Integer.class)) {
        fieldValue = Integer.parseInt(fieldValueTextRepresentation);
      }
      field.setAccessible(true);
      field.set(user, fieldValue);
    } catch (Exception e) {
      // Handle the problem as appropriate
      e.printStackTrace();
    }
  }
  
  /* Look for Jackson aliases */
  private Map<String, String> getAliases() {
    Map<String, String> fieldAliases = new HashMap<>();

    Field[] fields = User.class.getDeclaredFields();
    for (Field field: fields) {
      Annotation annotation = field.getAnnotation(JsonAlias.class);
      if (annotation != null) {
        String fieldName = field.getName();
        JsonAlias jsonAliasAnnotation = (JsonAlias) annotation;
        String[] aliases = jsonAliasAnnotation.value();
        for (String alias: aliases) {
          fieldAliases.put(alias, fieldName);
        }
      }
    }

    return fieldAliases;
  }
}

有了这个序列化器,给定一个user类,类似于:

import java.time.LocalDate;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

@JsonDeserialize(using = UserDeserializer.class)
public class User {
  private String firstName;
  private String lastName;
  private Integer age;
  private String address;
  @JsonAlias("dateofbirth")
  private LocalDate dateOfBirth;

  // Setters and getters omitted for brevity

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    User user = (User) o;

    if (firstName != null ? !firstName.equals(user.firstName) : user.firstName != null) return false;
    if (lastName != null ? !lastName.equals(user.lastName) : user.lastName != null) return false;
    if (age != null ? !age.equals(user.age) : user.age != null) return false;
    if (address != null ? !address.equals(user.address) : user.address != null) return false;
    return dateOfBirth != null ? dateOfBirth.equals(user.dateOfBirth) : user.dateOfBirth == null;
  }

  @Override
  public int hashCode() {
    int result = firstName != null ? firstName.hashCode() : 0;
    result = 31 * result + (lastName != null ? lastName.hashCode() : 0);
    result = 31 * result + (age != null ? age.hashCode() : 0);
    result = 31 * result + (address != null ? address.hashCode() : 0);
    result = 31 * result + (dateOfBirth != null ? dateOfBirth.hashCode() : 0);
    return result;
  }
{"firstName":"John","age":40,"dateofbirth":"1978-03-16"}
  public static void main(String... args) throws JsonProcessingException {
    User user = new User();
    user.setFirstName("John");
    user.setAge(40);
    user.setDateOfBirth(LocalDate.of(1978, Month.MARCH, 16));

    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

    String json = "{\"firstName\":\"John\",\"age\":40,\"dateofbirth\":\"1978-03-16\"}";

    User reconstructed = mapper.readValue(json, User.class);

    System.out.println(user.equals(reconstructed));
  }
public class KafkaConsumer {

    @Autowired
    private UserRepository userRepository;

    @KafkaListener(topics = "${spring.kafka.topic.name}")
    public void listen(@Payload(required = false) User user) {
        // Handle null value
        if (user == null) {
          // Consider logging the event
          // logger.debug("Null message received");
          System.out.println("Null message received");
          return;
        }

        // Continue as usual
        User  user = new User(user);
        UserRepository.save(user.getName(), user);
    }
}
 类似资料:
  • 我在dto类型的对象上设置了一个注释集,与在实体类型的对象上设置的注释集相同。注释对实体起作用,但对DTO类型的对象不起作用。 我在Springboot工作。Application.Properties 我需要能够获得在dto对象的字段上设置的约束注释,或者更确切地说是它的参数(但是在dto对象的情况下,我获得null,因为Spring可能不知道如何使用在dto对象的字段上设置的约束注释),在实体

  • 所以我有了这个网格: 在中有一个没有空格的超长字符串。是具有固定尺寸的占位符。这就产生了上述结果: 但将更改为会得到以下结果: 它会溢出其父级和屏幕大小。为什么它的行为不像Minmax? 码本

  • 问题内容: 我有单独的类来处理鼠标侦听器。但是当我在另一个类中使用它时,它不起作用,并且我还不知道如何解决这个问题。这是我的Handler类: DrawingCanvas类: StatusBar类: 和MouseEventGUI类: 当我运行该程序时,在状态栏中显示“未监听鼠标”(请参见MouseEventGUI类),希望它在画布中移动时显示鼠标光标的坐标。 [更新] 如果在同一个类中,则 可以

  • 我尝试使用另一个类中的OnClickListener,但不知为什么它会给我抛出一个错误。有人能帮我解决这个问题吗? 主要活动的一部分: 错误: java.lang.RuntimeException:无法启动activity ComponentInfo{com.example.user.project/com.example.user.project.MainActivity}:java.lang.

  • 问题内容: 正则表达式似乎还可以,因为第一行将子字符串正确替换为“ helloworld”,但是后者却不匹配,因为我看不到“ whynothelloworld?”。在控制台上 问题答案: 期望 整个 字符串匹配,而不仅仅是子字符串。 使用正则表达式匹配器对象的方法代替:

  • 问题内容: 我试图从Android手机上按本机后退按钮时取消CountDownTimer。因此,我想覆盖onBackPressed方法,以取消计时器并返回到另一个活动,但只能执行一次。(返回主活动,如主页按钮)。 这是代码的一部分: 问题答案: onBackPressed应该在主活动类中 还尝试在应用的清单文件中指定父项和子项活动,例如 并尝试使用类似的东西并重建您的应用程序 如果仍然无法正常运行