71

Is there a built-in way to only serialize the id of a child when using Jackson (fasterxml.jackson 2.1.1)? We want to send an Order via REST which has a Person reference. The person object however is quite complex and we could refresh it on the server side, so all we need is the primary key.

Or do I need a custom serializer for this? Or do I need to @JsonIgnore all other properties? Would that prevent the Person data from being sent back when requesting an Order object? I'm not sure yet if I'll need that but I'd like to have control over it if possible...

Elnur Abdurrakhimov
  • 44,533
  • 10
  • 148
  • 133
Pete
  • 10,720
  • 25
  • 94
  • 139

3 Answers3

133

There are couple of ways. First one is to use @JsonIgnoreProperties to remove properties from a child, like so:

public class Parent {
   @JsonIgnoreProperties({"name", "description" }) // leave "id" and whatever child has
   public Child child; // or use for getter or setter
}

another possibility, if Child object is always serialized as id:

public class Child {
    // use value of this property _instead_ of object
    @JsonValue
    public int id;
}

and one more approach is to use @JsonIdentityInfo

public class Parent {
   @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
   @JsonIdentityReference(alwaysAsId=true) // otherwise first ref as POJO, others as id
   public Child child; // or use for getter or setter

   // if using 'PropertyGenerator', need to have id as property -- not the only choice
   public int id;
}

which would also work for serialization, and ignore properties other than id. Result would not be wrapped as Object however.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
  • This is really useful. Thanks. When you use `@JsonIdentityReference(alwaysAsId=true)`, Jackson can't Deserialize it back, right? Is it possible to achieve the same functionality by writing custom Serializer/Deserializer? – miguelcobain Aug 29 '13 at 13:49
  • 1
    Correct -- if there is no object id to match, Jackson can not figure it out; so typically this option makes sense for serialize-only use case (where something else can piece it back together if need be). Custom (de)serializers can do anything you want, so theoretically yes. – StaxMan Sep 03 '13 at 16:08
  • 1
    @StaxMan can you please tell how I can use '@JsonIdentityReference(alwaysAsId=true' but I want to get Result wrapped in Object not as a normal attribute. – Waqas Oct 13 '15 at 16:44
  • @waqas716 I don't know of a way to do that. – StaxMan Oct 13 '15 at 18:37
  • @StaxMan sorry to chime in late to the party, any idea how to do this for a List or Collection of children? – Andrew Nov 11 '16 at 00:45
  • 1
    @Andrew not quite sure what you mean here. Basic ignoral does not care if child is `List` or POJO. But if you mean to apply to contents within `List`, this can not be done at this point; you'd need a wrapper object in between. – StaxMan Nov 11 '16 at 19:20
  • How to make same thing, but in situation, when parent and child the same entity? E.g. public class Item { private Item parent; private Set child; } – Nikolas Apr 13 '18 at 09:41
  • 1
    The last approach will make the first occurrence as a string `"id1"`, then the subsequent one as reference `*id1`. How to make it always as strings? – cakraww Apr 08 '19 at 07:11
  • @cakraww Jackson never generates reference "*id1" with JSON output so I don't know what you are referring to. – StaxMan Apr 25 '19 at 23:04
3

You can write a custom serializer like this:

public class ChildAsIdOnlySerializer extends StdSerializer<Child> {

  // must have empty constructor
  public ChildAsIdOnlySerializer() {
    this(null);
  }

  public ChildAsIdOnlySerializer(Class<Child> t) {
    super(t);
  }

  @Override
  public void serialize(Child value, JsonGenerator gen, SerializerProvider provider)
      throws IOException {
    gen.writeString(value.id);
  }

Then use it by annotating the field with @JsonSerialize:

public class Parent {
   @JsonSerialize(using = ChildAsIdOnlySerializer.class)
   public Child child;
}

public class Child {
    public int id;
}
cakraww
  • 2,493
  • 28
  • 30
1

Given for example a simple company with employees structure, in combination with

@JsonIgnore
@ManyToOne(cascade = {CascadeType.REFRESH, CascadeType.DETACH})
@JoinColumn(name = "child_id")

I would suggest to add the following:

@JsonProperty("child_id") for sending the child_id as property without it you won't get anything on client side, and- @JsonIgnoreProperties It will give the option to copy and paste the Json received from the server and send it back for example to update it. Without it you will get an exception when sending back, Or you will have to remove the child_id property from the received Json.

public class Company {
@OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
    private List<Employee> employees;
}

@JsonIgnoreProperties(value = {"company_id"},allowGetters = true)
public class Employee{
    @JsonIgnore
    @ManyToOne(cascade = {CascadeType.REFRESH, CascadeType.DETACH})
    @JoinColumn(name = "company_id")   
    public Company company

   @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, 
     property="id")
   @JsonIdentityReference(alwaysAsId=true) 
   @JsonProperty("company_id")
   }
   public Company getCompany() {
        return company;
    }
}
AryehSa
  • 11
  • 2