简体   繁体   中英

How can I unwrap a specific field in a JSON using Jackson?

I have a JSON payload that looks like this:

{
    "id": 32,
    "name": "[Sample] Tomorrow is today, Red printed scarf",
    "primary_image": {
      "id": 247,
      "zoom_url": "www.site.com/in_123__14581.1393831046.1280.1280.jpg",
      "thumbnail_url": "www.site.com/in_123__14581.1393831046.220.290.jpg",
      "standard_url": "www.site.com/in_123__14581.1393831046.386.513.jpg",
      "tiny_url": "www.site.com/in_123__14581.1393831046.44.58.jpg"
    }
  }

Can I unwrap a specific field and discard all the others? In other words, can I bind this directly to a POJO like this:

public class Product {

    private Integer id;
    private String name;
    private String standardUrl;
}

There are lots of ways. Do you need to deserialize, serialize or both?

One way to deserialize would be to use a creator method that takes the image as a tree node:

public static class Product {
    private Integer id;
    private String name;
    private String standardUrl;

    public Product(@JsonProperty("id") Integer id,
                   @JsonProperty("name") String name,
                   @JsonProperty("primary_image") JsonNode primaryImage) {
        this.id = id;
        this.name = name;
        this.standardUrl = primaryImage.path("standard_url").asText();
    }
}

The creator doesn't have to be a constructor, you could have a static method that is only used for Jackson deserialization.

You'd have to define a custom serializer to reserialize this, though (eg a StdDelegatingSerializer and a converter to wrap the string back up as an ObjectNode)

There are different ways to skin this cat, I hope you can use Jackson 2 for this, since it offers great ways to deserialize Json data, one of my favorites deserialization features is the one I'll show you here (using Builder Pattern ) because allows you to validate instances when they are being constructed (or make them immutable!). For you this would look like this:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.util.Map;

@JsonDeserialize(builder = Product.Builder.class)
public class Product {

private Integer id;

private String name;

private String standardUrl;

private Product(Builder builder) {
    //Here you can make validations for your new instance.

    this.id = builder.id;
    this.name = builder.name;

    //Here you have access to the primaryImage map in case you want to add new properties later.
    this.standardUrl = builder.primaryImage.get("standard_url");
}

@Override
public String toString() {
    return String.format("id [%d], name [%s], standardUrl [%s].", id, name, standardUrl);
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class Builder {

    private Integer id;

    private String name;

    private Map<String, String> primaryImage;

    public Builder withId(Integer id) {
        this.id = id;
        return this;
    }

    public Builder withName(String name) {
        this.name = name;
        return this;
    }

    @JsonProperty("primary_image")
    public Builder withPrimaryImage(Map<String, String> primaryImage) {
        this.primaryImage = primaryImage;
        return this;
    }

    public Product build() {
        return new Product(this);
    }
}
}

To test it I created this class:

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;

public class Test {
public static void main(String[] args) {


    String serialized = "{" +
                        "    \"id\": 32," +
                        "    \"name\": \"[Sample] Tomorrow is today, Red printed scarf\"," +
                        "    \"primary_image\": {" +
                        "      \"id\": 247," +
                        "      \"zoom_url\": \"www.site.com/in_123__14581.1393831046.1280.1280.jpg\"," +
                        "      \"thumbnail_url\": \"www.site.com/in_123__14581.1393831046.220.290.jpg\"," +
                        "      \"standard_url\": \"www.site.com/in_123__14581.1393831046.386.513.jpg\"," +
                        "      \"tiny_url\": \"www.site.com/in_123__14581.1393831046.44.58.jpg\"" +
                        "    }" +
                        "  }";


    ObjectMapper objectMapper = new ObjectMapper();

    try {

        Product deserialized = objectMapper.readValue(serialized, Product.class);

        System.out.print(deserialized.toString());
    } catch (IOException e) {
        e.printStackTrace();
    }
}

The output is (using the override toString() method in Product :

id [32], name [[Sample] Tomorrow is today, Red printed scarf], standardUrl [www.site.com/in_123__14581.1393831046.386.513.jpg].

There are two ways to get the response you required. For both methods, we are going to use JsonView.

Create two types of JsonView:

public interface JViews {
    public static class Public { }
    public static class Product extends Public { }
}

First method

@JsonView(JViews.Public.class)
public class Product {
    private Integer id;
    private String name;
    @JsonIgnore
    private Image primaryImage;
    @JsonView(JViews.Product.class)
    public String getStandardUrl{
       return this.primaryImage.getStandardUrl();
    }
}

Second way

Using Jackson's @JsonView and @JsonUnwrapped together.

@JsonView(JViews.Public.class)
public class Product {
    private Integer id;
    private String name;

    @JsonUnwrapped
    private Image primaryImage;
}
public class Image {
   private String zoomUrl;
   @JsonView(JViews.Product.class)
   private String standardUrl;
}

@JsonUnwrapped annotation flattens your nested object into Product object. And JsonView is used to filter accessible fields. In this case, only standardUrl field is accessible for Product view, and the result is expected to be:

{
    "id": 32,
    "name": "[Sample] Tomorrow is today, Red printed scarf",
    "standard_url": "url"
}

If you flatten your nested object without using Views, the result will look like:

{
    "id": 32,
    "name": "[Sample] Tomorrow is today, Red printed scarf",
    "id":1,
    "standard_url": "url", 
    "zoom_url":"",
    ...
}

Jackson provided @JsonUnwrapped annotation.

See below link:

http://jackson.codehaus.org/1.9.9/javadoc/org/codehaus/jackson/annotate/JsonUnwrapped.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM