【问题标题】:Deserialize JSON where key and value are stored separately反序列化 JSON,其中键和值分别存储
【发布时间】:2021-05-01 13:16:07
【问题描述】:

我有一个 JSON 结构,其中键和值是这样存储的

[
  {
    "key": "firstName",
    "value": "John"
  },
  {
    "key": "lastName",
    "value": "Smith"
  }
]

我想反序列化数组,以便每个键都是一个属性名称,并分配适当的值。

public class Customer {

  private String firstName;
  private String lastName;

  public String getFirstName() {
    return this.firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return this.lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
}

我曾想过创建一个自定义反序列化器来接受一组客户属性,并根据属性名称手动设置每个属性。我担心这种方法可能会随着添加更多属性而变得脆弱,并且无法长期维护。

有人知道我可能忽略的任何有助于反序列化的 Jackson 注释吗?

【问题讨论】:

  • 为你的类写一个自定义的StdDeserializer,它使用字段反射?
  • 某些键已经不符合 java 命名约定。如果我能让作者确认所有可能的密钥都是合法标识符,这不是一个大问题。现在,让我们假设情况并非如此。
  • 我建议这个 cmets 线程正确地应该以“答案”开头。来吧,准确地展示你的想法。

标签: java json jackson deserialization json-deserialization


【解决方案1】:

Jackson 将能够从JSON ObjectMap 反序列化POJO。您有一个 mini-Maps 列表,其中每个 mini-Map 只包含一个属性。您需要:

  • 将输入负载反序列化为List<Map<String, Object>>
  • 将其转换为单个Map 对象
  • 转换为Customer实例

简单示例:

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class JsonMiniMapsApp {

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

        ObjectMapper mapper = new ObjectMapper();
        // read as list of maps
        List<Map<String, Object>> entries = mapper.readValue(jsonFile, new TypeReference<List<Map<String, Object>>>() {
        });

        // collect all values to single map
        Map<Object, Object> map = entries.stream().collect(Collectors.toMap(e -> e.get("key"), e -> e.get("value")));

        // convert to POJO
        Customer customer = mapper.convertValue(map, Customer.class);

        System.out.println(customer);
    }
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Customer {

    private String firstName;
    private String lastName;
}

上面的代码打印:

Customer(firstName=John, lastName=Smith)

【讨论】:

    【解决方案2】:

    只需用@JsonProperty注释属性

    public class Customer {
    
    @JsonProperty("key")
    private String firstName;
    @JsonProperty("value")
    private String lastName;
    
    .... 
    }
    

    如果您想在序列化期间保留名称,则应注释 setter 和 getter 而不是字段。

    这里是一个例子:Different names of JSON property during serialization and deserialization

    【讨论】:

      【解决方案3】:

      这可以通过 Jackson 以非常通用的方式解决。

      首先,您需要一个小的 Java 类来表示任何 键/值对(例如{"key":"firstName","value":"John"}) 从您的 JSON 输入。我们就叫它Entry吧。

      public class Entry {
      
          private String key;
          private Object value;
      
          // getters and setters (omitted here for brevity)
      }
      

      使用上面的类,您可以反序列化 JSON 输入 (在您的问题中给出)到 Entry 对象的数组。

      ObjectMpper objectMapper = new ObjectMapper();
      File file = new File("example.json");
      Entry[] entries = objectMapper.readValue(file, Entry[].class);
      

      然后你可以把这个Entry[]数组转换成Map&lt;String, Object&gt;, 并进一步到Customer 对象 (使用问题中给出的Customer 类)。

      Map<String, Object> map = Arrays.stream(entries)
              .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
      Customer customer = objectMapper.convertValue(map, Customer.class);
      

      【讨论】:

        猜你喜欢
        • 2015-08-31
        • 1970-01-01
        • 1970-01-01
        • 2018-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多