2

This is the json I need (generated by Ember, but works on the server):

{"customer":{"partyType":"jdo.party.model.Organization","name":"asdf"}}

This is the json I'm getting:

{"id":null,"partyType":"jdo.party.model.Company","name":"Test Company","firstName":"","lastName":""}

I need the jersey client to emit the correct json, and I'm not sure why. The client code:

client = ClientBuilder.newClient();
client.register(JacksonFeature.class);
client.register(new LoggingFilter(Logger.getGlobal(),true));
resource = client.target("http://localhost:8090/crm/api").path("/customers");
CustomerDto dto = new CustomerDto(null, "jdo.party.model." + type, name, "", "");
response = resource.request()
                .accept(APPLICATION_JSON)
                .post(Entity.json(dto), CustomerDto.class);

The class I'm converting to json on both the client & server:

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "customer")
@JsonRootName("customer")
public class CustomerDto implements Serializable {

    @XmlAttribute
    private UUID id;
    @XmlAttribute
    @NotEmpty
    private String partyType;
    @XmlAttribute
    private String name;
    @XmlAttribute
    private String firstName;
    @XmlAttribute
    private String lastName;

    public CustomerDto(Party party) {
        if (party instanceof Person) {
            partyType = ((Person) party).getClass().getCanonicalName();
            firstName = ((Person) party).getFirstName();
            lastName = ((Person) party).getLastName();
        } else if (party instanceof Organization) {
            partyType = ((Organization) party).getClass().getCanonicalName();
            name = ((Organization) party).getName();
        } else {
            throw new IllegalArgumentException(
                    String.format("Customer must be person or Organization.  %s was passed instead.",
                            party.getClass().getCanonicalName()));
        }
        id = party.getId();
    }

    @AssertTrue
    public boolean hasName() {
        return isNotBlank(name) || isNotBlank(lastName);
    }

    public String getPartyType() {
        return partyType;
    }

    public String getName() {
        return name;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + ((partyType == null) ? 0 : partyType.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        CustomerDto other = (CustomerDto) obj;
        if (firstName == null) {
            if (other.firstName != null)
                return false;
        } else if (!firstName.equals(other.firstName))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (lastName == null) {
            if (other.lastName != null)
                return false;
        } else if (!lastName.equals(other.lastName))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (partyType == null) {
            if (other.partyType != null)
                return false;
        } else if (!partyType.equals(other.partyType))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "CustomerDto [id=" + id + ", partyType=" + partyType + ", name=" + name + ", firstName=" + firstName
                + ", lastName=" + lastName + "]";
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public UUID getId() {
        return id;
    }

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    public CustomerDto() {
        super();
    }

    public CustomerDto(UUID id, String partyType, String name, String firstName, String lastName) {
        super();
        this.id = id;
        this.partyType = partyType;
        this.name = name;
        this.firstName = firstName;
        this.lastName = lastName;
    }

}
Jim Barrows
  • 3,634
  • 1
  • 25
  • 36

2 Answers2

1

So there's two things we need to accomplish:

  1. Ignore null values in the DTO
  2. Wrap the JSON object in a root value.

1. Ignore Nulls

You could...

The easiest way is to annotate the DTO with

@JsonInclude(JsonInclude.Include.NON_NULL)

This will tell Jackson to ignore null values. You will also need to make sure to pass null as the constructor values instead of "" (which is not the same thing).

You could...

If you want to set this property globally, you could configure it on the ObjectMapper

ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

There are a couple ways we can register this ObjectMapper to be used in our application

  1. Configure it in a ContextResolver, as seen here. Then register the ContextResolver with the client

    client.register(new ObjectMapperContextResolver());
    
  2. Configure the mapper with a Jackson provider instance

    client.register(new JacksonJaxbJsonProvider(objectMapper, null));
    

2. Wrap JSON

Only one way I know, and that's to configure the ObjectMapper.

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);

The actual value used is determined by your @JsonRootName annotation value.

See above for ways to configure the mapper to be used with the application.


Note, to keep support for Jaxb annotations, it's possible you need to register the jaxb module with the ObjectMapper

mapper.registerModule(new JaxbAnnotationModule());

This I am not sure of, as using the JacksonFeature, JAXB annotation is already supported. But I don't know if providing an ObjectMapper overrides that or not. Same with using the JacksonJaxbJsonProvider explicitly. So you may just want to test things out.

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • I went looking for configuration on the server side to wrap the json as I needed.. and missed the annotation configuration where i did the second options. Thanks so much!!!! – Jim Barrows Jul 25 '15 at 03:51
  • @JimBarrows The one thing I wasn't quite sure about in your question, is why the server would expect the JSON to be wrapped, i.e. `{"customer":{ }}`. Normally it would just expect `{ }`. If you wrap it in the client, then you would need to unwrap it on the server, in which case, you could use `mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true)`. But if you did not have this in the first place, I wasn't sure why you would even need to wrap it on the client side. – Paul Samsotha Jul 25 '15 at 04:32
  • As I said above. I had configured the server as peskillet has said. I had forgotten that I had done it via java, as opposed to a config file. Hence my confusion. – Jim Barrows Jul 25 '15 at 13:22
0

If you need only the partyType and name attribute in the JSON , the why are you annotating the other fields in the CustomerDto.java class

Anirban Roy
  • 111
  • 3
  • Edited to try and clear it up. The working json is generated by Ember, and accepted by the server. The not-working is generated from the DTO ( that does work on the server), by Jackson. – Jim Barrows Jul 25 '15 at 13:25