2

I have a JSON structure where the key and value are stored like this

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

I would like to deserialize the array so that each key is a property name, and the appropriate value assigned.

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;
  }
}

I have thought of creating a custom deserializer to accept an array of customer attributes, and manually setting each property based on the attribute name. My concern is this approach could be become brittle with the addition of more properties, and is not maintainable in the long term.

Does anyone know of any Jackson annotations that I may have overlooked that would aid in this deserialization?

CrizNap
  • 21
  • 2
  • Write a custom `StdDeserializer` for your class which uses reflection for fields? – BeUndead Jan 27 '21 at 16:54
  • Some of the keys already do not match java naming conventions. Which is not a huge issue if I can get the author to confirm that all possible keys are legal identifiers. For now, let's assume that is not the case. – CrizNap Jan 27 '21 at 17:21
  • I suggest that this thread of comments properly should begin with an "Answer." Go ahead and show exactly what you have in mind. – Mike Robinson Jan 27 '21 at 19:22

3 Answers3

1

Just annotate the properties with @JsonProperty

public class Customer {

@JsonProperty("key")
private String firstName;
@JsonProperty("value")
private String lastName;

.... 
}

if you want to keep the names during serialization, you should annotate the setter and getter instead of the field.

Here is an example: Different names of JSON property during serialization and deserialization

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
1

This can be solved in a quite generic way with Jackson.

First, you will need a small Java class representing any key/value pair (like for example {"key":"firstName","value":"John"}) from your JSON input. Let's call it Entry.

public class Entry {

    private String key;
    private Object value;

    // getters and setters (omitted here for brevity)
}

Using the class above you can deserialize the JSON input (as given in your question) to an array of Entry objects.

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

Then you can convert this Entry[] array to a Map<String, Object>, and further to a Customer object (using the Customer class as given in your question).

Map<String, Object> map = Arrays.stream(entries)
        .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
Customer customer = objectMapper.convertValue(map, Customer.class);
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
1

Jackson will be able to deserialise a POJO from JSON Object or Map. You have a list of mini-Maps where each mini-Map contains exactly one property. You need to:

  • Deserialise input payload to List<Map<String, Object>>
  • Transform it to single Map object
  • Convert to Customer instance

Simple example:

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;
}

Above code prints:

Customer(firstName=John, lastName=Smith)
Michał Ziober
  • 37,175
  • 18
  • 99
  • 146