accept multiple JSON formats in web service - java

In my REST web service I need to accept JSON that can have two different structures.
Currently I have:
#Path("/")
public class MyAppResource {
...
#Context private HttpServletRequest request;
...
#POST
#Produces(MediaType.APPLICATION_JSON)
public MyResponseItem check(MyRequestItem body) {
...
}
}
where
public class MyRequestItem {
...
#JsonCreator
public MyRequestItem(#JsonProperty("data") ArrayList<TextItem> data) {
...
}
...
}
and
class TextItem {
...
#JsonCreator
public TextItem(#JsonProperty("count") int count,
#JsonProperty("text") String text) {
...
}
...
}
So it accepts JSON of the form {"data":[{"count":123,"text":"abc"},...]}.
In addition to the above format I need to accept this format: {"data":["abc",...]}. That is, I think I need to change TextItem so that it can either be a String or a class as above.
How to achieve this?

If you don't mind it to be the same class for both cases(TextItem), the easiest option for you is to add 1 more TextItem constructor with single string argument.
Here is demo:
public class Main {
public static String json1 = "{\"data\":[{\"count\":123,\"text\":\"abc\"}]}";
public static String json2 = "{\"data\":[\"abc\"]}";
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json1, MyRequestItem.class));
System.out.println(mapper.readValue(json2, MyRequestItem.class));
}
#Data // lombok.Data;
#ToString // lombok.ToString;
public static class MyRequestItem {
private List<TextItem> data;
#JsonCreator
public MyRequestItem(#JsonProperty("data") ArrayList<TextItem> data) {
this.data = data;
}
}
#Data
#ToString
public static class TextItem {
private int count;
private String text;
#JsonCreator
public TextItem(#JsonProperty("count") int count,
#JsonProperty("text") String text) {
this.count = count;
this.text = text;
}
// this is the only thing you need to add to make it work
public TextItem( String text) {
this.text = text;
}
}
}
Result:
MyRequestItem(data=[TextItem(count=123, text=abc)])
MyRequestItem(data=[TextItem(count=0, text=abc)])

Related

Jackson custom value for nested class's field

Good day everyone,
Im struggling with serializing object with nested object having custom name with # JsonProperty.
So, here it is:
public class Wrapper {
public final Payload payload;
public Wrapper(String name){
this.payload = new Payload(name);
}
public static final class Payload{
#JsonProperty("customName")
public final String name;
#JsonCreator
public Payload(#JsonProperty ("customName") String name){
this.name = name;
}
}
}
So, in every test I see non-custom name - "name". I added getter with #JsonProperty without any success.
My test class:
#JsonTest
public class SerializeWrapperTest {
#Test
void whenSerialiseThanCorrect() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Wrapper wrapper =
new Wrapper("name");
String json = objectMapper.writeValueAsString(wrapper);
Assertions.assertEquals("{"payload":{"customName":"name"}}",json);
}
}
Test results:
Expected :{"payload":{"customName":"name"}}
Actual :{"payload":{"name":"name"}}
org.opentest4j.AssertionFailedError.
Edited.
If I understood it correct , you need to have a custom property in json for the field , "name".
For that you need to do following -
public class Wrapper {
public final Payload payload;
public Wrapper(String name){
this.payload = new Payload(name);
}
public static final class Payload{
#JsonProperty("customName")
public final String name;
public Payload(String name){
this.name = name;
}
}
JUnit -
public class WrapperTest {
#Before
public void setUp() throws Exception {
}
#After
public void tearDown() throws Exception {
}
#Test
public void whenSerialiseThanCorrect() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Wrapper wrapper =
new Wrapper("Ajeet");
String json = objectMapper.writeValueAsString(wrapper);
System.out.println(json);
}
}
}

Issue With #JsonProperty on Method

I currently have my POJO class as such for deserializing a json source.
public class OpenBuilding extends Building {
#JsonProperty("BuildingPostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Where the parent class is as such
public abstract class Buidling {
protected String postcode;
public String getPostcode() {
return this.postcode;
}
}
My issue is that the String postcode isn't getting mapped at all. It works when using the annotation on the field. However since it is an inherited field and I have other children of Building, which use different property names for the same data, I cannot have it implemented in that way.
For example:
public class DirectedBuilding extends Building {
#JsonProperty("Pseudo_PostCode")
#Override
public String getPostcode() {
return super.getPostcode();
}
}
Perhaps try defining a constructor with #JsonCreator.
class Parent {
private final String foo;
public Parent(final String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
}
class Child extends Parent {
#JsonCreator
public Child(#JsonProperty("foo") final String foo) {
super(foo);
}
#JsonProperty("foo")
public String getFoo() {
return super.getFoo();
}
}
public static void main(String[] args) throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
final Child toSerialize = new Child("fooValue");
// Serialize the object to JSON
final String json = objectMapper.writer()
.withDefaultPrettyPrinter()
.writeValueAsString(toSerialize);
// Prints { "foo" : "fooValue" }
System.out.println(json);
// Deserialize the JSON
final Child deserializedChild = objectMapper.readValue(json, Child.class);
// Prints fooValue
System.out.println(deserializedChild.getFoo());
}

JSON parse with Objectmapper JAVA

This is the JSON string:
{
"d":{
"results":[
{
"__metadata":{
"uri":"http://blabla1",
"type":"type1"
},
"Synonym":"miami"
},
{
"__metadata":{
"uri":"http://blabla2",
"type":"type2"
},
"Synonym":"florida"
}
]
}
}
This is the code:
public class Test{
#JsonIgnoreProperties(ignoreUnknown = true)
public static class d {
public List<Results> results;
public d() {
results = new ArrayList<Results>();
}
public static class Results {
public Metadata __metadata;
public String Synonym;
public Results() {}
}
public static class Metadata {
public String uri;
public String type;
public Metadata() {}
}
}
}
With the following mapper:
ObjectMapper mapper = new ObjectMapper();
Test.d readValue = mapper.readValue(jsonString, Test.d.class);
for (Test.d.Results k : readValue.results) {
System.out.println("synonym: "+k.Synonym);
}
It gives me no error, just an empty arraylist of results...
p.s. i have made a lot of changes in between time, sorry for the inconvenience
you must create an object that fits with the jSon answer, something like this (not tested):
class d {
public List<Results> results;
public d() {
}
}
class Results {
public Metadata metadata;
public String synonym;
public Results() {
}
}
class Metadata {
public String uri;
public String type;
public Metadata() {
}
}
Hope it helps!
I have managed to solve it.
i forgot to make setters and getters for class 'd'.
public class Test {
private d d;
public d getD() {return d;}
public void setD(d d) {this.d = d;}
public static class d{
private List<Results> results;
public List<Results> getResults() {return results;}
public void setResults(List<Results> results) {this.results = results;}
}
public static class Results {
public Metadata __metadata;
public String Synonym;
}
public static class Metadata {
private String uri;
private String type;
public String getUri() {return uri;}
public void setUri(String uri) {this.uri = uri;}
public String getType() {return type;}
public void setType(String type) {this.type = type;}
}
}
This is the map:
Test test = mapper.readValue(json, KeyPhrase.class);
System.out.println("cp");
for(Test.Results res : test.getD().getResults()){
System.out.println(res.Synonym);
}

Serialize generic field from java object to json

I've a generic field in User.java. I want to use the value of T in json.
public class User<T> {
public enum Gender {MALE, FEMALE};
private T field;
private Gender _gender;
private boolean _isVerified;
private byte[] _userImage;
public T getField() { return field; }
public boolean isVerified() { return _isVerified; }
public Gender getGender() { return _gender; }
public byte[] getUserImage() { return _userImage; }
public void setField(T f) { field = f; }
public void setVerified(boolean b) { _isVerified = b; }
public void setGender(Gender g) { _gender = g; }
public void setUserImage(byte[] b) { _userImage = b; }
}
and mapper class is:
public class App
{
public static void main( String[] args ) throws JsonParseException, JsonMappingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
Name n = new Name();
n.setFirst("Harry");
n.setLast("Potter");
User<Name> user = new User<Name>();
user.setField(n);
user.setGender(Gender.MALE);
user.setVerified(false);
mapper.writeValue(new File("user1.json"), user);
}
}
and the json output is :
{"field":{"first":"Harry","last":"Potter"},"gender":"MALE","verified":false,"userImage":null}
In the output, i want Name to be appeared in place of field. How do i do that. Any help?
I think what u ask is not JSON's default behavior. Field name is the "key" of the json map, not the variable name. U should rename the field or make some String process to do it.
private T field;
change the above to this:
private T name;
You need a custom serializer to do that. That's a runtime data transformation and Jackson has no support for data transformation other than with a custom serializer (well, there's wrapping/unwrapping of value, but let's not go there). Also, you will need to know in advance every type of transformation you want to apply inside your serializer. The following works:
public class UserSerializer extends JsonSerializer<User<?>> {
private static final String USER_IMAGE_FIELD = "userImage";
private static final String VERIFIED_FIELD = "verified";
private static final String FIELD_FIELD = "field";
private static final String NAME_FIELD = "name";
#Override
public void serialize(User<?> value, JsonGenerator jgen, SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartObject();
if (value.field instanceof Name) {
jgen.writeFieldName(NAME_FIELD);
} else {
jgen.writeFieldName(FIELD_FIELD);
}
jgen.writeObject(value.field);
jgen.writeStringField("gender", value._gender.name());
jgen.writeBooleanField(VERIFIED_FIELD, value._isVerified);
if (value._userImage == null) {
jgen.writeNullField(USER_IMAGE_FIELD);
} else {
jgen.writeBinaryField(USER_IMAGE_FIELD, value._userImage);
}
jgen.writeEndObject();
}
}

Json Jackson deserialization without inner classes

I have a question concerning Json deserialization using Jackson.
I would like to deserialize a Json file using a class like this one:
(taken from http://wiki.fasterxml.com/JacksonInFiveMinutes)
public class User
{
public enum Gender { MALE, FEMALE };
public static class Name {
private String _first, _last;
public String getFirst() { return _first; }
public String getLast() { return _last; }
public void setFirst(String s) { _first = s; }
public void setLast(String s) { _last = s; }
}
private Gender _gender;
private Name _name;
private boolean _isVerified;
private byte[] _userImage;
public Name getName() { return _name; }
public boolean isVerified() { return _isVerified; }
public Gender getGender() { return _gender; }
public byte[] getUserImage() { return _userImage; }
public void setName(Name n) { _name = n; }
public void setVerified(boolean b) { _isVerified = b; }
public void setGender(Gender g) { _gender = g; }
public void setUserImage(byte[] b) { _userImage = b; }
}
A Json file can be deserialized using the so called "Full Data Binding" in this way:
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(new File("user.json"), User.class);
My problem is the usage of the inner class "Name". I would like to do the same thing without using inner classes. The "User" class would became like that:
import Name;
import Gender;
public class User
{
private Gender _gender;
private Name _name;
private boolean _isVerified;
private byte[] _userImage;
public Name getName() { return _name; }
public boolean isVerified() { return _isVerified; }
public Gender getGender() { return _gender; }
public byte[] getUserImage() { return _userImage; }
public void setName(Name n) { _name = n; }
public void setVerified(boolean b) { _isVerified = b; }
public void setGender(Gender g) { _gender = g; }
public void setUserImage(byte[] b) { _userImage = b; }
}
This means to find a way to specify to the mapper all the required classes in order to perform the deserialization.
Is this possible? I looked at the documentation but I cannot find any solution.
My need comes from the fact that I use the Javassist library to create such classes, and it does not support inner or anonymous classes.
Thank you in advance
There should be no difference between the static inner class Name, and the top-level class of the same name. The Jackson runtime should not be able to meaningfully distinguish between the two situations.
Have you tried moving the Name class out of User, changing it into a top-level class? It should still work as before.
edit: I just tried this, and it works fine when Name is a top-level class. The example had it as an inner class for the sake of brevity, I suspect.
mr. Skaffman's answer is right on. The only additional thing to mention is that unlike JAXB, Jackson does not generally require you to specify classes you operate on, except for the root class (and not always even that, if you use Polymorphic Handling).

Categories

Resources