I have a rest service which send JSON. Before sending, I want to change one field, for example add n to price. n is a parameter, which comes from the client. How I can pass n to JsonSerializer without AOP and Reflection?
You can do with SimpleModule.
Create SimpleModule and create your Serializer object manually and pass parameter to constructor.
Here is example
Let say we have User model class like:
package com;
public class User {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
private String name;
private int number;
}
Here is custom Serializer class like :
package com;
public class UserSerializer extends JsonSerializer<User>{
private String n;
public UserSerializer(String n){
this.n = n;
}
#Override
public void serialize(User user, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
if(user != null){
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("name", "Hello, "+user.getName()+" : value of n : "+n);
jsonGenerator.writeEndObject();
}
}
}
Here is your main class :
public class Application {
public static void main(String[] args) throws Exception{
User user = new User();
user.setName("nitin");
String n = "1111"; //this value comes from API
SimpleModule module = new SimpleModule();
module.addSerializer(User.class, new UserSerializer(n));
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(module);
String xml = xmlMapper.writeValueAsString(user);
System.out.println(xml);
}
}
and your out put should be like :
<User1 xmlns=""><name>Hello, nitin : value of n : 1111</name></User1>
If you want output in json, then use ObjectMapper.
Related
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);
}
}
}
I have this Java class:
class Car {
int mileage;
int id;
}
When I tell gson to serialize it, it of course serializes it to:
{
"mileage": 123,
"id": 12345678
}
But what if I want to serialize it to:
{
"mileage": "123",
"id": "12345678"
}
Assuming changing my members from int to String, is not an option, is there a way to tell gson to serialize those int members as strings to the json file?
There are likely many ways to achieve what you desire.
I will share two ways.
FIRST - Using Custom Serialization
SECOND - Using JsonAdapter Annotation - More Simple
Using a custom serialization
public static class CarSerializer implements JsonSerializer<Car> {
public JsonElement serialize(final Car car, final Type type, final JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add("mileage", new JsonPrimitive(Integer.toString(car.getMileage())));
result.add("id", new JsonPrimitive(Integer.toString(car.getId())));
return result;
}
}
To call this, simply adapt your code or use the following code with a constructor
Car c = new Car(123, 123456789);
com.google.gson.Gson gson = new
GsonBuilder().registerTypeAdapter(Car.class, new CarSerializer())
.create();
System.out.println(gson.toJson(c));
The output should be
{"mileage":"123","id":"12345678"}
Full Code for Example 1:
public class SerializationTest {
public static class Car {
public int mileage;
public int id;
public Car(final int mileage, final int id) {
this.mileage = mileage;
this.id = id;
}
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
public String getMileage() {
return mileage;
}
public void setMileage(final String mileage) {
this.mileage = mileage;
}
}
public static class CarSerializer implements JsonSerializer<Car> {
public JsonElement serialize(final Car car, final Type type, final JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add("mileage", new JsonPrimitive(Integer.toString(car.getMileage())));
result.add("id", new JsonPrimitive(Integer.toString(car.getId())));
return result;
}
}
public static void main(final String[] args) {
Car c = new Car(123, 123456789);
com.google.gson.Gson gson = new
GsonBuilder().registerTypeAdapter(Car.class, new CarSerializer())
.create();
System.out.println(gson.toJson(c));
}
}
Using a #JsonAdapter annotation
Use the JsonAdapter Annotation on the Car class
#JsonAdapter(CarAdapter.class)
public class Car {
public int mileage;
public int id;
}
Create the Custom Adapter
public class CarAdapter extends TypeAdapter<Car> {
#Override
public void write(JsonWriter writer, Car car) throws IOException {
writer.beginObject();
writer.name("mileage").value(car.getMileage());
writer.name("id").value(car.getId());
writer.endObject();
}
#Override
public Car read(JsonReader in) throws IOException {
// do something you need
return null;
}
}
To serialize, using this method, use something like this
Car c = new Car(123, 123456789);
Gson gson = new Gson();
String result = gson.toJson(c);
Printing result in this case should output
{"mileage":"123","id":"12345678"}
You may try it this way:
new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
.registerTypeAdapter(Integer.class, (JsonSerializer<Integer>)
(integer, type, jsonSerializationContext) -> new
JsonPrimitive(String.valueOf(integer)))
.excludeFieldsWithoutExposeAnnotation().create();
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)])
I created a JacksonList class and JacksonListSerializer (extending JSonSerializer) for resolve a issue with collections and inheritance classes. Works fine.
The problem is that serializer ignore JsonIdentityInfo "rules". I have mapping exceptions like "Already had POJO for id (java.lang.Integer) .."
I put example below:
JacksonList.java
#JsonSerialize(using = JacksonListSerializer.class)
class JacksonList<E> extends ArrayList<E> {
private static final long serialVersionUID = 1L;
}
JacksonListSerializer.java
public class JacksonListSerializer extends JsonSerializer<JacksonList<?>> {
#Override
public void serialize(JacksonList<?> list, JsonGenerator generator, SerializerProvider provider)
throws IOException, JsonProcessingException {
generator.writeStartArray();
if (list != null) {
for (Object item : list) {
generator.writeObject(item);
}
}
generator.writeEndArray();
}
}
FirstItem.java
public class FirstItem {
private Son son;
public Son getSon() {
return son;
}
public void setSon(Son son) {
this.son = son;
}
}
Son.java
#JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="#id", scope=Son.class)
public class Son {
public Son(String text) {
this.text = text;
}
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Main.java
ObjectMapper mapper = new ObjectMapper();
FirstItem f1 = new FirstItem();
FirstItem f2 = new FirstItem();
JacksonList<FirstItem> jacksonList = new JacksonList<FirstItem>();
List<FirstItem> list = new ArrayList<FirstItem>();
Son son = new Son();
f1.setSon(son);
f2.setSon(son);
list.add(f1);
list.add(f2);
jacksonList.add(f1);
jacksonList.add(f2);
System.out.println(mapper.writeValueAsString(jacksonList));
System.out.println(mapper.writeValueAsString(list));
Output:
{"JacksonList":[{"FirstItem":{"id":1,"son":{"type":"com.Son","#id":1}}},{"FirstItem":{"id":2,"son":{"type":"com.Son","#id":1}}}]}
{"ArrayList":[{"id":1,"son":{"type":"com.Son","#id":1}},{"id":2,"son":1}]}
As you can see in the second case (print of ArrayList) the information of the Son class are not duplicated. But in the first case the object son is put two times.
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();
}
}