How to map single JSON field to multiple JAVA field with GSON? - java

I'm trying to parse JSON using Retrofit and Gson, but I need to map one JSONfield
's value to multiple JAVA fields inside bean class.
Here is an example code:
class A{
#SerializedName("name");
private String name;
#SerializedName("name");
private String fullName;
}
This is the error I'm seeing: class A declares multiple JSON fields named name. Is there any way to do this?
Update: Please avoid suggesting removing one field from the bean or making changes into getter and setter. The project is huge, and the field is being used later in many other cases, so I don't want to mess with the structure. The question is pretty much clear and on the point.

No need to declare JSON for fullname use name value with fullname in setter gatter.
class A{
#SerializedName("name");
private String name;
private String fullName;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFullName() {
return name;
}
public void setFullName(String fullName) {
this.name = fullName;
}
}

Related

How to create JSON file extracting specific object fields, basing on object with all fields annotated with #JsonProperty - JAVA

I have a class like this in one of the microservices (I cannot change it):
public class MyClass {
String name;
Integer age;
String gender;
List<SomeObject> some objects;
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("age")
public String getAge() {
return name;
}
#JsonProperty("gender")
public String getGender() {
return name;
}
#JsonProperty("someObjects")
public List<SomeObject> getSomeObjects() {
return someObjects;
}
}
My task is to create JSON file basing on data from that class, but this file should only contain
'age' and 'name' property, and from SomeObject I should also extract part of the data to create a list.
I don't know how can I 'turn off' the properties, that I don't needed. Is it possible?
Yes, you can using Jackson ObjectMapper.
Add the dependency, with Maven, inside your pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
Create your custom entity without the fields you don't need on your client side :
public class CustomPerson {
String name;
Integer age;
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("age")
public String getAge() {
return name;
}
}
Use ObjectMapper to save any object or list of objects of CustomPerson POJO :
ObjectMapper objectMapper = new ObjectMapper();
// one person object saved in person.json file :
objectMapper.writeValue(new File("path/to/person.json"), person);
// many person objecs saved in persons.json file :
objectMapper.writeValue(Paths.get("persons.json").toFile(), persons);
The rest is trivial, you'll get it easily.

Is it an anti-pattern to use Jackson's `#JsonCreator` on empty constructors?

I repeatedly saw code like the following, where the empty constructor was deleted by well-intentioned developers (because it appeared unused), which then broke Jackson serialization (caught by tests later on):
public class Person {
private String name;
private int age;
// appears unused – only used by Jackson
public Person() {}
// used in code
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
I'm thinking of adding a #JsonCreator annotation to the empty constructor to make it more obvious that the constructor is used by Jackson to prevent developers from removing it:
public class Person {
private String name;
private int age;
#JsonCreator // <--------
public Person() {}
// used in code
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Is this a good idea or does this have unintended consequences (i. e. changing semantics compared to the no-annotation case)?
Or is this use of #JsonCreator an anti-pattern and there is a better way to achieve this (short of converting the class to a record)?
Reading more documentation about this I and regarding all the comments to the question and other answer(s) I came to the conclusion that it really is an anti-pattern to use the #JsonCreator for the default constructor.
The Javadoc (e.g. from version 2.13) specifies that a ctor annotated with that annotation should have at least one parameter. (It doesn't explicitly say so, but a ctor without arguments would be useless more or less.)
NOTE: when annotating creator methods (constructors, factory methods), method must either be:
Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
So for your problem I would suggest that you get rid of the default ctor and instead annotate at least one of the remaining ctor(s) used in code as well properly. Jackson can use that and the IDE will not mark it as unused.
It also could save you from workarounds for not properly or completely initialized objects.
In your example this could be
public class Person {
private String name;
private int age;
#JsonCreator
public Person(#JsonProperty("name") String name, #JsonProperty("age") int age) {
this.name = name;
this.age = age;
}
// other getters / setters / business logic come here
}
It's an anti pattern, because the default constructor is the #JsonCreator default and everyone knows it - it is useless code.
You don't need to prevent developers from removing it because your functional tests will fail if they do so.

Assign variables from nested JSON string using Jackson

Wondering if anyone can help me figure out away to assign the body context to my description String variable.
Here is my JSON string
{"requirement":{"description":{"body":"This is a text"}}}
public class Requirement implements Serializable {
private String description;
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
}
I know I can use #JsonProperty("description") but my description is nested with different context. In this case I only care about the body.
If you don't want to have the class with same structure as the json, you'll have to first unpack the description object and extract body:
public class Requirement {
private String body;
#JsonProperty("description")
private void unpackNested(Map<String,Object> description) {
this.body = (String)description.get("body");
}
}
Your data structure actually looks like this
class Requirement{
private Description description;
}
class Description{
private String body;
}
just add proper #JsonProperty and you will be fine.
In general, every json Object is a separate class (unless you map to plan maps)

How to dynamically build a class in java

All:
I am pretty new in Java. What I want to do is build a java data model object dynamically according to the CSV fields it reads in, like:
file1.csv
Name, Age, Email, Url
Allen, 30, allen#mail.com, http://allen.mail.com
Bob, 20, bob#mail.com, http://bob.mail.com
Firstly I read in the header, and according to the header, I want to create a class InfoModel which has those field names as its member variable.
Could anyone help? Thanks.
You could create the sourcecode dynamically, compile and load the class using the java API. But it would be pretty complicated to use the resulting class. You should simply use a hashmap for the variables in InfoModel and create the class before runtime. Would be simpler to use and more efficient.
You can create a corresponding Bean Class having Name, Age, Email, Url as member variable
Class Employee{
private String name;
private String age;
private String email;
private String url;
public Employee(String name,String age,String email,String url){
this.name=name;
this.age=age;
//and so on
}
// their getters and setters
}
// their getters and setters
}
Now as soon as you read the CSV lines after header , you can create Object of Employee Class
new Employee(name,age,email,url);
What I want to do is build a java data model object dynamically according to the CSV fields it reads in.
Ok, let's look at the CSV input again.
Name, Age, Email, Url
Allen, 30, allen#mail.com, http://allen.mail.com
Bob, 20, bob#mail.com, http://bob.mail.com
Here's the class that you want to generate.
package com.ggl.testing;
public class InfoModel {
private final String name;
private final int age;
private final String email;
private final String url;
public InfoModel(String name, int age, String email, String url) {
this.name = name;
this.age = age;
this.email = email;
this.url = url;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getEmail() {
return email;
}
public String getUrl() {
return url;
}
}
So, here is the main question you have to answer to generate the text of this class from the CSV file.
How do I determine what type of data is in the CSV file? In other words, how do I determine that Age is an int? How do I determine that Name is a String?
Once you've figured out the data types, you can output the text of the InfoModel class by using a lot of StringBuilders.
See my article, Writing Java Code that Writes Java Code, for an example of how to write a Java application that writes Java classes.
Just an idea... If the fields are limited, for example if you only need various combinations of fields, you could create an interface for each field (getter and setter) and then creaty a Proxy object for all of them, handling the data, for example, with an internal HashMap. This will get you an object that implements all of the interfaces, but of course, only in a very specific way.

'dynamic'-like java annotations?

I have a pojo that is dependent on annotations. It has predefined fields as well as a Set that contains user provided fields:
public class MyPOJO implements Document {
private String id;
private LocalString name;
private LocalString desc;
private List<Field> fields;
public MyPOJO(final String id,
final LocalString name,
final LocalString desc,
final List<Field> fields) {
this.id = id;
this.name = name;
this.desc = desc;
this.fields = fields;
}
public String getId() {
return id;
}
#Indexed(searchable = false, stored = true)
public LocalString getName() {
return name;
}
#Indexed(searchable = true)
public LocalString getDescription() {
return desc;
}
public List<Field> getFields() {
return fields;
}
}
MyPOJO is a 'generic' object, ie, the developer (or consumer) of MyPOJO has fields that are not predefined in MyPOJO and therefore the developer needs to place these additional fields the in attribute 'fields'. The problem arises from the fact that each object in the Set fields needs to have its own annotations to indicate whether the particular field is either stored or searchable in order to remain consistent with the predefined attributes, such as name.
I can think of two options:
For each additional field, the developer will have to create an
anonymous class implementing the interface Field and inside this
anonymous class, the developer will declare the applicable
annotations.
the Set 'fields' contains a complex object of fieldname, fieldvalue
and annotations as shown below. I can't figure out how to invoke the constructor for Field. The below code does not compile but it is intended as pseudo-code to signify what I am trying to do.
Field myfield1 = new Field("dateofBirth", new Date(), new ArrayList({Index.stored, Index.searchable});
Field myfield2 = new Field("model", "330i", new ArrayList({Index.stored});
There is no construct to pass annotations as a parameter: new ArrayList({Index.stored}.
public class Field {
private String name;
private Object value;
Collection<Annotation> annotations;
public Field(final String name, final Object value, Collection<Annotation> annotations;) {
this.name = name;
this.value = value;
this.annotations = Collections.unmodifiableCollection(annotations);
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
I'm not particularly excited with either option and hoping someone can give me some pointers
If you need an extensible object model, I'd say a POJO design is just setting yourself up for extra work as opposed to exposing a metamodel.
That said, what you could do is have clients of the API subclass MyPOJO, and annotate the properties they define in their subclasses. You would then use reflection to go through all JavaBeans properties of the objects you're receiving and determine the annotations on the getters - similarly to how JPA works.

Categories

Resources