jersey/jaxb unmarshalling a java object, but fields not populated - java

I have a Jersey server side code which takes a Java object as a body parameter. This Java object lets say Preferences is defined as below.
#XmlRootElement(name = "preferences", namespace = "http://arjun.test.com/tests/1.0")
public class Preferences {
String field1;
String field2;
public Preferences() {
}
#XmlElement(name = "field-1", namespace = "http://arjun.test.com/tests/1.0")
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
#XmlElement
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
}
My problem is: while sending the data from client I am populating both fields, but somehow in the server the first field value is always null, if I change the field1 XML annotation to the same as field2, then it works fine.
Can someone please let me know what mistake am I doing.
The Jersey server method is producing and consuming the JSON objects.

Related

How to use objectMapper.readValue with records? [duplicate]

I am trying to see if I can replace my existing Pojos with the new Record classes in Java 14. But unable to do so. Getting following error:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot
construct instance of com.a.a.Post (no Creators, like default
construct, exist): cannot deserialize from Object value (no delegate-
or property-based Creator)
I get that the error is saying the record has no constructors, but from what I see the record class takes care of it in the background and relevant getters are also set in the background (not getters exactly but id() title() and so on without the get prefix). Is it cos Spring has not adopted the latest Java 14 record yet? Please advice. Thanks.
I am doing this in Spring Boot version 2.2.6 and using Java 14.
The following works using the usual POJOs.
PostClass
public class PostClass {
private int userId;
private int id;
private String title;
private String body;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
Method to call rest service which works now as I am using the above POJO.
public PostClass[] getPosts() throws URISyntaxException {
String url = "https://jsonplaceholder.typicode.com/posts";
return template.getForEntity(new URI(url), PostClass[].class).getBody();
}
But if I switch to following where I am using record instead, I am getting the above error.
The new record class.
public record Post(int userId, int id, String title, String body) {
}
Changing the method to use the record instead which fails.
public Post[] getPosts() throws URISyntaxException {
String url = "https://jsonplaceholder.typicode.com/posts";
return template.getForEntity(new URI(url), Post[].class).getBody();
}
EDIT:
Tried adding constructors as follows to the record Post and same error:
public record Post(int userId, int id, String title, String body) {
public Post {
}
}
or
public record Post(int userId, int id, String title, String body) {
public Post(int userId, int id, String title, String body) {
this.userId = userId;
this.id = id;
this.title = title;
this.body = body;
}
}
It is possible with some Jackson Annotations, which cause Jackson to use fields instead of getters. Still far less verbose than a pre-Java 14 class (without Lombok or similar solutions).
record Foo(#JsonProperty("a") int a, #JsonProperty("b") int b){
}
This probably works because according to https://openjdk.java.net/jeps/359:
Declaration annotations are permitted on record components if they are
applicable to record components, parameters, fields, or methods.
Declaration annotations that are applicable to any of these targets
are propagated to implicit declarations of any mandated members.
See also: When is the #JsonProperty property used and what is it used for?
It is also possible to make use #JsonAutoDetect
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
If configuring the Objectmapper to use field Visibility globally, this annotation on class level is not needed.
See also: How to specify jackson to only use fields - preferably globally
Example:
public class Test {
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper om = new ObjectMapper();
System.out.println(om.writeValueAsString(new Foo(1, 2))); //{"a":1,"b":2}
System.out.println(om.writeValueAsString(new Bar(3, 4))); //{"a":3,"b":4}
}
record Foo(#JsonProperty("a") int a, #JsonProperty("b") int b){
}
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
record Bar(int a, int b){
}
}
There is also a Github issue for that feature: https://github.com/FasterXML/jackson-future-ideas/issues/46
This is slated for jackson 2.12
https://github.com/FasterXML/jackson-future-ideas/issues/46
The compiler generates the constructor and other accessor method for a Record.
In your case,
public final class Post extends java.lang.Record {
public Post(int, int java.lang.String, java.lang.String);
public java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public int userId();
public int id();
public java.lang.String title();
public java.lang.String body();
}
Here you can see that there is not default constructor which is needed got Jackson. The constructor you used is a compact constructor,
public Post {
}
You can define a default/no args constructor as,
public record Post(int userId, int id, String title, String body) {
public Post() {
this(0,0, null, null);
}
}
But Jackson uses Getter and Setters to set values. So in short, you can not use Record for mapping the response.
EDIT as PSA: Jackson can properly serialize and deserialize records as of 2.12 which has been released.
Use the parameter names module for jackson, https://github.com/FasterXML/jackson-modules-java8/tree/master/parameter-names (make sure the compiler sets -parameters) or add `#JsonProperty("name") to each field in the record
add #JsonCreator to the constructor. I can't tell if the inheritance will work properly, so you might have to explicitly declare the constructor and annotate it.
If a public accessor method or (non-compact) canonical constructor is declared explicitly, then it only has the annotations which appear on it directly; nothing is propagated from the corresponding record component to these members.
From https://openjdk.java.net/jeps/384
So add
new ObjectMapper().registerModules(new ParameterNamesModule())
and try
#JsonCreator record Value(String x);
or something like
record Value(String x) {
#JsonCreator
public Value(String x) {
this.x = x;
}
}
or all the way to
record Value(#JsonProperty("x") String x) {
#JsonCreator
public Value(#JsonProperty("x") String x) {
this.x = x;
}
}
This is how I get immutable pojos with lombok and jackson to work, and I don't see why records wouldn't work under the same format. My setup is Jackson parameter names module, -parameters compiler flag for java 8 (I don't think this is required for like jdk9+), #JsonCreator on the constructor. Example of a real class working with this setup.
#Value
#AllArgsConstructor(onConstructor_ = #JsonCreator)
public final class Address {
private final String line1;
private final String line2;
private final String city;
private final String region;
private final String postalCode;
private final CountryCode country;
}

Convert list of jsonNode objects to another class, but add a new parent field

I am very new to java and I hope my question is not too stupid and has enough info for you guys to help me out.
I have a list of jsonNodes, each of them is in the following format:
{"field1":value1, "field2":value2, "field3":value3, "notneeded1":value4, "notneeded2":value5}
I am currently using a class like the following and converting it to list
#JsonIgnoreProperties(ignoreUnknown = true)
class customClass:
String field1;
String field2;
String field3;
Using TypeReference to convert the list of jsonNode to list of this class...
What I want to do is add a few of these fields within an another field
{"parentfield":{"field1":value1, "field2":value2}, "field3":value3}
How do I do this using this class?
AFIU you want this:
#JsonIgnoreProperties(ignoreUnknown = true)
class CustomClass {
public String field1;
public String field2;
public String field3;
public CustomClass2 parentfield;
}
#JsonIgnoreProperties(ignoreUnknown = true)
class CustomClass2 {
public String field1;
public String field2;
}
Then depends on your code, to set the values of the fields in an object of CustomClass2 with fields from an object of CustomClass.

Parsing two isomorphous XML schemas into one class structure using JAXB

Consider two isomorphous XML schemas. By isomorphism here I mean that these two schemas have identical structures except attributes and tags names. More specifically I have live example when was schema, say A, and its copy B, where all tags and attribute names were translated from English into national lamguage equivalents.
For example, as input we can have two different variants of one object:
<tag_1_v1>
<tag_2_v1 id="blabla" name="xxxxx">
Some value1
</tag_2_v1>
<tag_3_v1 id="alalala" name="yyyyy">
Some value2
</tag_3_v1>
</tag_1_v1>
and
<tag_1_v2>
<tag_2_v2 special_id_2="blabla" name="xxxxx">
Some value1
</tag_2_v2>
<tag_3_v2 id="alalala" special_name_2="yyyyy">
Some value2
</tag_3_v2>
</tag_1_v2>
The problem is to map these two schemas on single class structure, say
class Tag1 {
Tag2 tag2;
Tag3 tag3;
}
class Tag2 {
String id;
String name;
String value;
}
class Tag3 {
String id;
String name;
String value;
}
There are various ideas how to workaround this issue, but all of them aren't so convinient, as any possibility to use single JAXB annotation scheme on same class structure. They are:
create two different class-sets and then copy values from objects of
one schema into another;
create own SAX parser implementation and "translate" inside it tag and attribute names into appropriate ones;
use own preprocessor of XML and use string replacement (will not work if id and attributes name aren't identical within all schema).
Since each <tag_i> can have different attributes, a clean solution would be to use inheritance:
Create an abstract class Tag1 that is inherited by Tag1V1 and Tag1V2. Factor all the common code into Tag1.
The same would go Tag2 and Tag3.
To get you started, here would be an implementation of Tag2:
#XmlRootElement
#XmlSeeAlso({Tag2V1.class, Tag2V2.class})
abstract class Tag2 {
private String name;
private String content;
#XmlAttribute(name = "name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlValue
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
#XmlRootElement(name = "tag_2_v1")
class Tag2V1 extends Tag2 {
private String id;
#XmlAttribute(name = "id")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
#XmlRootElement(name = "tag_2_v2")
class Tag2V2 extends Tag2 {
private String specialId2;
#XmlAttribute(name = "special_id_2")
public String getSpecialId2() {
return specialId2;
}
public void setSpecialId2(String specialId2) {
this.specialId2 = specialId2;
}
}

Remove a field from entity when passing to Jersey + Jackson

I'm using Jersey + Jackson (built in in Dropwizard) to create a series of web services. I directly map objects in Json by passing them to Response object in Jersey:
myObject object = new myObject(fields...);
return Response.ok(object).build();
Fields are correctly annotated in myObject class with JsonProperty("fieldName").
But, in case I have a field that I need to store to database (ex: a password hash), but I do not want to pass in request responses, how can I remove that field when passing the entity to Response object?
I can't annotate the field with JsonIgnore, otherwise that field won't be serialized at all when I map the Json to database (ElasticSearch).
One option is to simply set the field to null. To configure the ObjectMapper to ignore the field in the JSON altogether when the field is null, you can just do
#Override
public void run(YourConfiguration configuration,
Environment environment) throws Exception {
...
environment.getObjectMapper().setSerializationInclusion(Include.NON_NULL);
}
As an aside, this security reason a one of the reasons to use DTOs (data transfer objects), an extra entity "view" layer that separates the representation we send out from the persistence layer (db entity object). It may seem redundant to create another object with the same/similar attributes, but the security padding is worth it.
Also, though not an official release yet, Dropwizard 0.8.0 uses Jersey 2, which introduced Entity Filtering, which allows us filter out the data we don't want sent out, without the need to create DTOs. Just thought I'd mention it.
You should use both JsonIgnore and JsonProperty to achieve this.
public class User {
private String name;
private String password;
#JsonProperty
public void setPassword(String password) {
this.password = password;
}
#JsonIgnore
public String getPassword() {
return this.password;
}
}
#JsonProperty on setter method will be used in serialization & JsonIgnore on getter method will be used in deserialization.
Actually #Manikandan answer should work for you. See Only using #JsonIgnore during serialization, but not deserialization
In worst case you may try to implement JsonSerializer.
public class MyObjectSerializer extends JsonSerializer<MyObject> {
#Override
public void serialize(MyObject value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeString(value.getField1());
jgen.writeString(value.getField2());
/* and not include field that you don't want to serialize */
jgen.writeEndObject();
}
}
#JsonSerialize(using = MyObjectSerializer.class)
public class MyObject {
String field1;
Integer field2;
String fieldNotToBeSerialized;
public String getField1() {
return field1;
}
public void setField1(String field1) {
this.field1 = field1;
}
public Integer getField2() {
return field2;
}
public void setField2(Integer field2) {
this.field2 = field2;
}
public String getFieldNotToBeSerialized() {
return fieldNotToBeSerialized;
}
public void setFieldNotToBeSerialized(String fieldNotToBeSerialized) {
this.fieldNotToBeSerialized = fieldNotToBeSerialized;
}
}

Verify array with custom objects

I have a class:
public class MyCustomObject {
private String field1;
private String field2;
}
And I've created array of MyCustomObject:
MyCustomObject[] array = new MyCustomObject[]{new MyCustomObject()};
My goal is to verify elements of this array using hamcrest matchers. I've tried the following approach:
assertThat(array, allOf(hasItemInArray(hasProperty("field1", equalTo("value1")))), hasItemInArray(hasProperty("field2", equalTo("value2")))));
But unfortunatly it does not work.
In which way the array of custom objects can be verified?
I would change your array to an ArrayList just for testing purposes:
List<MyCustomObject> customObjects = Arrays.asList(array);
And then assert with the Hamcrest hasItems Matcher if the expected items are present in the list:
assertThat(customObjects, hasItems(myCustomObject1, myCustomObject2));
Have you tried adding getters to your class? That did the trick for me.
public class MyCustomObject {
private String field1;
private String field2;
public String getField1() {
return field1;
}
public String getField2() {
return field2;
}
}

Categories