2

Scenario

  • Jersey/JacksonJson for my RESTful web services requests.
  • An entity A that has properties X,Y,Z.
  • 2 RESTful requests.
  • Request 1 should return entity A with properties X,Y in the JSON response
  • Request 2 should return entity A with properties X,Y,Z in the JSON response
  • Entity A is configured such that property Z is using @JsonIgnore so it is not returned in the JSON response

Problem

How do I return property Z in Request 2 if it is set to @JsonIgnore in the entity? Is there are better way to do this dynamically besides using @JsonIgnore? Below is some "Demonstration" code to help clarify my question.

@Entity
Class A implements Serializable {
  String X;
  String Y;
  String Z;

  @JsonIgnore
  public String getZ() {
    return Z;
  }
}

@Path("form")
Class Request {
    @GET
    @Path("request1")
    @Produces({"application/json"})
    public A request1() {
      return A;
    }

    @GET
    @Path("request2")
    @Produces({"application/json"})
    public A request2() {
      return A;
    }
}
bickster
  • 1,292
  • 17
  • 18

4 Answers4

2

You can use @JsonView annotations.

Something like:

public class Views {
    public static class BasicView {
    }

    public static class FullView extends BasicView {
    }
}

Then in class A,

  @JsonView(Views.BasicView.class)
  public String getX() {
    return X;
  }


  @JsonView(Views.BasicView.class)
  public String getY() {
    return Y;
  }


  @JsonView(Views.FullView.class)
  public String getZ() {
    return Z;
  }

When you have to return the JSON, you will have to serialize them using custom view-based ObjectWriters though:

public static final ObjectWriter basicObjectWriter = objectMapper
            .writerWithView(Views.BasicView.class);
public static final ObjectWriter fullObjectWriter = objectMapper
            .writerWithView(Views.FullView.class);

Then, if you do basicObjectWriter.writeValueAsString(responseObject), it will write only X and Y. fullObjectWriter.writeValueAsString(responseObject) will write X, Y and Z.

Hari Menon
  • 33,649
  • 14
  • 85
  • 108
  • This is a valid solution as well, but I would like to stay away from manually serializing objects. Thanks. – bickster Oct 01 '13 at 12:27
0

You can try using Jackson's BeanSerializerModifier for this purpose. In order to exclude properties dynamically, you would need to extend BeanSerializerModifier and implement the changeProperties method appropriately. The following code snippet applies to the example in question:

public class CustomBeanSerializerModifier extends BeanSerializerModifier
{
    private String path;

    public CustomBeanSerializerModifier(String path)
    {
        this.path = path;
    }

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
            BeanDescription desc, List<BeanPropertyWriter> inputProperties)
    {
        List<BeanPropertyWriter> outputProperties = new ArrayList<>();
        for (BeanPropertyWriter property : inputProperties) {
            if (path.startsWith("request1") && property.getType().getRawClass().equals(A.class)) {
                if (property.getName().equals("Z")) {
                    continue;
                }
            }
            outputProperties.add(property);
        }
        return super.changeProperties(config, desc, outputProperties);
    }
}

Plug this in Jackson's ObjectMapper as follows (using a Module):

public class CustomModule extends Module
{
    private String path;

    public CustomModule(String path)
    {
        this.path = path;
    }

    @Override
    public void setupModule(SetupContext context)
    {
        context.addBeanSerializerModifier(new CustomBeanSerializerModifier(path));
    }

    // Other overridden methods
}

and

ObjectMapper objectMapper = new ObjectMapper();
String path = getJerseyPath();
CustomModule module = new CustomModule(path);
objectMapper.registerModule(module);

// Call objectMapper.writeValue() to serialize stuff

This question deals with using custom Jackson code (in the form of ObjectMapper) with Jersey.

Hope this helps!

Community
  • 1
  • 1
Jackall
  • 1,120
  • 1
  • 9
  • 18
0

If you don't need to send null values,you can set the variables that you don't want to send as null, and set Jackson to ignore null properties. This way, on the request1 you can set Z as null. Other way is to model your classe's using extend's, but i don´t know if Jackson can parse easily in such scenario.

Vítor Marques
  • 349
  • 4
  • 11
  • This is what I ended up doing. I annotated all my domain classes with @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL). I removed all @JsonIgnore annotations. – bickster Oct 01 '13 at 12:25
0

You say you are using Jersey/JAX-RS, so you Yoga should be of particular interest.

Yoga extends JAX-RS and SpringMVC RESTful servers to provide GData and LinkedIn style field selectors.

  • Choose which fields you want to see at call-time
  • Navigate entity relationships in a single call for complex views
Community
  • 1
  • 1
theon
  • 14,170
  • 5
  • 51
  • 74