Problem writing a builder for a class in java - java

I have problem writing a builder for a class in java. It always gives me an error saying could not find symbol.
class Company {
private String name;
private String address;
private String contact;
private int sizeOfEmployee;
private Date createdTime;
#Override
public String toString() {
return "Company{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", contact='" + contact + '\'' +
", sizeOfEmployee=" + sizeOfEmployee +
", createdTime=" + createdTime +
'}';
}
public static class builder {
private String name;
private String address;
private String contact;
private int sizeOfEmployee;
private Date createdTime;
public builder Name (String name){
this.name = name;
return this;
}
public builder address(String address){
this.address = address;
return this;
}
public builder contact(String contact){
this.contact = contact;
return this;
}
public builder size(int sizeOfEmployee){
this.sizeOfEmployee = sizeOfEmployee;
return this;
}
public builder date(Date createdTime){
this.createdTime = createdTime;
return this;
}
}
}
I would like to write a builder that could print out my Company class. Could anyone tell me why my builder is not working? I search for how to write a builder and try to copy the method. But it's not working here. It gives me an error saying error can not find symbol.
If I call
Company abc = Company.builder().name("abc").address("Virginia").createdTime(new Date()).sizeOfEmployee(300).contact("12345").build();
It will print out
Company{name='abc', address='Virginia', contact='12345', sizeOfEmployee=300, createdTime=Wed Oct 21 11:50:41 EDT 2020}

You are calling static method builder 'Company.builder()' which does not exists.
You can solve the problem this way:
public class Company {
private String name;
private String address;
public static CompanyBuilder builder() {
return new CompanyBuilder();
}
private static class CompanyBuilder {
private final Company company = new Company();
public CompanyBuilder name(String name){
company.name = name;
return this;
}
public CompanyBuilder address(String address){
company.address = address;
return this;
}
public Company build() {
return company;
}
}
public static void main(String[] args) {
System.out.println(Company.builder().name("name").address("address").build());
}
}
I would suggest Lombok for java projects. With one annotation #Builder including the Lombok plugin and dependency you can solve the problem, also you can use a lot of features instead of boilerplate code.
import lombok.Builder;
import lombok.ToString;
#Builder
#ToString
public class Company {
private String name;
private String address;
public static void main(String[] args) {
System.out.println(Company.builder().name("name").address("address").build());
}
}

Related

"JSON parse error: Cannot construct instance of (although at least one Creator exists): cannot deserialize from Object value - SpringBoot

I am using SpringBoot with Mongo database and I am trying to save embedded documents into database.
I have this model:
Profile.java
#Data
#Document
public class Profile {
public final City city;
public final String imageId;
public Profile(City city,
String imageId) {
this.city = city;
this.imageId = imageId;
}
#Override
public String toString() {
return "Profile{" +
", city=" + city +
", imageId='" + imageId + '\'' +
'}';
}
private static boolean atLeast(int numChars, String s) {
if (s == null) {
return false;
}
var str = s.strip();
return str.length() >= numChars;
}
public static ProfileBuilder builder() {
return new ProfileBuilder();
}
public static final class ProfileBuilder {
public City city;
public String imageId;
private ProfileBuilder() {
}
public ProfileBuilder withCity(City city) {
this.city = city;
return this;
}
public ProfileBuilder withImageId(String imageId) {
this.imageId = imageId;
return this;
}
public Profile build(){
return new Profile(city, imageId);
}
}
}
City.java
public class City {
public final String name;
public City(String name) {
this.name = name;
}
#Override
public String toString() {
return "City{" +
", name='" + name + '\'' +
'}';
}
}
ProfileController.java
#RequestMapping( method = RequestMethod.POST)
public Profile addUser(#RequestBody Profile profile) {
return profileService.addProfile(profile);
}
and with postman I am sending this JSON
{
"city":{
"name":"Atena"
},
"imageId" : "Doe",
}
}
But I am getting following error:
"JSON parse error: Cannot construct instance of `domain.City` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator);"
There are at least two solutions.
Add #JsonCreator to constructor and #JsonProperty to its arguments (to instruct Jackson how to substitute JSON items into constructor in proper order)
class Profile {
...
#JsonCreator
public Profile(#JsonProperty("city") City city,
#JsonProperty("imageId") String imageId) {
this.city = city;
this.imageId = imageId;
}
...
}
(+ same for City class)
Unfinal class properties and provide default no-arg constructor (along with existing all-arg constructor).
class Profile {
public City city;
public String imageId;
public Profile() {
}
public Profile(City city, String imageId) {
this.city = city;
this.imageId = imageId;
}
}
(+ same for City class)
Test
class Test {
public static void main(String[] args) throws JsonProcessingException {
String json = "{\"city\":{\"name\":\"Atena\"},\"imageId\":\"Doe\"}";
Profile p = new ObjectMapper().readValue(json, Profile.class);
System.out.println(p);
}
}
Output:
Profile{, city=City{, name='Atena'}, imageId='Doe'}
In classes with only one attribute, to deserialize an object Json need a nos argument constructor from that class.
In your class city you need a nos arg constructor add this to your class you need:
public City () {}

#JsonMerge with list of objects and builder

I am trying to merge JSON objects with the new #JsonMerge annotation. I found a sample online that works when I run it in my IDE. Here's a snippet to run:
#Test
void mergeTest() throws IOException {
final Employee employee = new Employee("Serializon", new Address("Street 1", "City 1", "ZipCode1"));
final Employee newEmployee = new Employee("Serializon", new Address("Street 2", "City 2", "ZipCode2"));
assertThat(employee.getAddress().getCity()).isEqualTo("City 1");
final ObjectMapper objectMapper = new ObjectMapper();
final Employee mergedEmployee = objectMapper.readerForUpdating(employee).readValue(JSONUtil.toJSON(newEmployee));
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedEmployee));
assertThat(newEmployee.getAddress().getCity()).isEqualTo("City 2");
assertThat(mergedEmployee.getAddress().getCity()).isEqualTo("City 2");
}
public class Employee {
private String name;
#JsonMerge
private Address address;
public Employee(final String name, final Address address) {
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
}
public class Address {
private String street;
private String city;
private String zipCode;
public Address(final String street, final String city, final String zipCode) {
this.street = street;
this.city = city;
this.zipCode = zipCode;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZipCode() {
return zipCode;
}
}
When I try to reproduce this with my own class, it fails with the following error:
Deserialization of [simple type, class package.State] by passing existing instance (of package.State) not supported
My class in question is a POJO with some lists and primitive properties, all with getters. It is constructed using a builder and is immutable. It looks like this:
#JsonDeserialize(builder = State.Builder.class)
public class State {
private final String id;
#JsonMerge
private final List<Module> modules;
protected State(final Builder builder) {
this.id = builder.id;
this.modules = builder.modules;
}
public static Builder builder() {
return new Builder();
}
public String getId() {
return id;
}
public List<Module> getModules() {
return modules;
}
#JsonIgnoreProperties(ignoreUnknown = true)
public static final class Builder {
private String id;
private List<Module> modules;
private Builder() {
}
public Builder withId(final String id) {
this.id = id;
return this;
}
public Builder withModules(final List<Module> modules) {
this.modules = modules;
return this;
}
public State build() {
return new State(this);
}
}
}
The merge annotation states the following:
Merging is only option if there is a way to introspect current state: if there is accessor (getter, field) to use. Merging can not be enabled if no accessor exists or if assignment occurs using a Creator setter (constructor or factory method), since there is no instance with state to introspect.
So I thought perhaps the builder might the problem, but retrofitting the Employee/Address sample with a builder still works:
#Test
void mergeTest() throws IOException {
final Employee employee = Employee.newBuilder()
.withName("Serializon")
.withAddress(Address.newBuilder()
.withStreet("Steet 1")
.withCity("City 1")
.withZipCode("ZipCode1")
.build())
.build();
assertThat(employee.getAddress().getCity()).isEqualTo("City 1");
final Employee newEmployee = Employee.newBuilder()
.withName("Serializon")
.withAddress(Address.newBuilder()
.withStreet("Steet 2")
.withCity("City 2")
.withZipCode("ZipCode2")
.build())
.build();
final ObjectMapper objectMapper = new ObjectMapper();
final Employee mergedEmployee = objectMapper.readerForUpdating(employee).readValue(JSONUtil.toJSON(newEmployee));
System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(mergedEmployee));
assertThat(newEmployee.getAddress().getCity()).isEqualTo("City 2");
assertThat(mergedEmployee.getAddress().getCity()).isEqualTo("City 2");
}
#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
private String name;
#JsonMerge
private Address address;
private Employee(final Builder builder) {
name = builder.name;
address = builder.address;
}
public static Builder newBuilder() {
return new Builder();
}
public String getName() {
return name;
}
public Address getAddress() {
return address;
}
public static final class Builder {
private String name;
private Address address;
private Builder() {
}
public Builder withName(final String name) {
this.name = name;
return this;
}
public Builder withAddress(final Address address) {
this.address = address;
return this;
}
public Employee build() {
return new Employee(this);
}
}
}
#JsonDeserialize(builder = Address.Builder.class)
public class Address {
private String street;
private String city;
private String zipCode;
private Address(final Builder builder) {
street = builder.street;
city = builder.city;
zipCode = builder.zipCode;
}
public static Builder newBuilder() {
return new Builder();
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getZipCode() {
return zipCode;
}
public static final class Builder {
private String street;
private String city;
private String zipCode;
private Builder() {
}
public Builder withStreet(final String street) {
this.street = street;
return this;
}
public Builder withCity(final String city) {
this.city = city;
return this;
}
public Builder withZipCode(final String zipCode) {
this.zipCode = zipCode;
return this;
}
public Address build() {
return new Address(this);
}
}
}
Finally I tried to have a list of addresses instead, and accepting the list in the builder as withAddresses instead. So, for brevity:
#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
#JsonMerge
private List<Address> addresses;
public static final class Builder {
public Builder withAddresses(final List<Address> addresses) {
this.addresses = addresses;
return this;
}
}
}
And when I run the testcase again, this fails with the same error as my own code:
Deserialization of [simple type, class se.itab.locker.core.util.Employee] by passing existing instance (of se.itab.locker.core.util.Employee) not supported
What is actually going on here, and can I resolve it somehow or is this an unsupported use case or bug?
Update
So I found that this works:
//#JsonDeserialize(builder = Employee.Builder.class)
public class Employee {
#JsonCreator
public Employee(final Employee employee) {
name = employee.name;
addresses = employee.addresses;
stringAddresses = employee.stringAddresses;
}
But then serializing causes an infinite loop instead.

Java object scope between methods

I'm having issues with this code running, I'm trying to get the program to print the strings below by using input from the other classes. As you can see, the info put into the new Bride and Location objects are being put in to a Wedding Object and then I need to try and retrieve the details from the wedding object and display it on screen like so:
Wedding data:
Bride: Amy Cronos, age: 29
Location: South Rd, suburb: Tonsley
but I am instead met with 4 identical errors relating to the place.getName, place.getSuburb() etc. etc. that say
Main.java:6: error: cannot find symbol
System.out.println("Location"+place.getStreet()+", suburb:
"+place.getsuburb());
symbol: variable place
location: class Main
I'm pretty sure this has something to do with the scope, but cant work out what I need to do.
What is causing this error and how do I fix it?
Here is the code:
public class WeddingDetails {
public static void main(String[] args) {
Bride person = new Bride("Amy Cronos", 29);
Location place = new Location("Tonsley", "South Rd");
Wedding wed = new Wedding(person, place);
show(wed);
}
public static void show(Wedding wed) {
System.out.println("Wedding data:");
System.out.println("Bride: " + person.getName() + ", age: " + person.getAge());
System.out.println("Location: " + place.getStreet() + ", suburb: " + place.getSuburb());
}
public static class Location {
private String suburb;
private String street;
Location(String suburb, String street) {
this.suburb = suburb;
this.street = street;
}
public String getSuburb() {
return suburb;
}
public String getStreet() {
return street;
}
}
public static class Bride {
private String name;
private int age;
Bride(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public static class Wedding {
private Bride person;
private Location place;
Wedding(Bride person, Location place) {
this.person = person;
this.place = place;
}
public Bride getBride() {
return person;
}
public Location getPlace() {
return place;
}
}
}
The issue here is your println statements are trying to access methods within objects, but by calling those methods on the wrong object. You should be accessing the Bride and Location objects with the Wedding class' getters (getBride() and getPlace(). The complete call would be wed.getBride().getName() and wed.getPlace().getStreet() so on.
Corrected code is below. NOTE: for the purposes of being able to compile all of the code inside one class, I added the static keyword to the Bride, Location and Wedding class declarations. You can just remove the static and copy and paste each class back into your .java files.
public class WeddingDetails {
public static void main(String[] args) {
Bride person = new Bride("Amy Cronos", 29);
Location place = new Location("Tonsley", "South Rd");
Wedding wed = new Wedding(person, place);
show(wed);
}
public static void show(Wedding wed) {
System.out.println("Wedding data:");
System.out.println("Bride: " + wed.getBride().getName() + ", age: " + wed.getBride().getAge());
System.out.println("Location: " + wed.getPlace().getStreet() + ", suburb: " + wed.getPlace().getSuburb());
}
public static class Location {
private String suburb;
private String street;
Location(String suburb, String street) {
this.suburb = suburb;
this.street = street;
}
public String getSuburb() {
return suburb;
}
public String getStreet() {
return street;
}
}
public static class Bride {
private String name;
private int age;
Bride(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public static class Wedding {
private Bride person;
private Location place;
Wedding(Bride person, Location place) {
this.person = person;
this.place = place;
}
public Bride getBride() {
return person;
}
public Location getPlace() {
return place;
}
}
}

How to convert Json to Java Object, Deserializing Json

data_user = "{"id":1,"lastName":"lastName","name":"name","school":{"id":1}}"
public class School {
private int id;
private String name;
}
public class User {
private int id;
private String lastName;
private String name;
private School school;
}
How to deserialize Json data_user to java object User?
I tried with Gson :
Gson gson = new Gson();
User user = gson.fromJson(data_user, User.class)
But I have an error with this code because the Json contains a school which hasn't the school's name.
How Can I serialize the Json to java Object?
School.java
public class School {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "School [id=" + id + ", name=" + name + "]";
}
}
User.java
public class User {
private int id;
private String lastName;
private String name;
private School school;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public School getSchool() {
return school;
}
public void setSchool(School school) {
this.school = school;
}
#Override
public String toString() {
return "User [id=" + id + ", lastName=" + lastName + ", name=" + name
+ ", school=" + school + "]";
}
}
Main.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.testgson.beans.User;
public class Main {
private static Gson gson;
static {
gson = new GsonBuilder().create();
}
public static void main(String[] args) {
String j = "{\"id\":1,\"lastName\":\"lastName\",\"name\":\"ignacio\",\"school\":{\"id\":1}}";
User u = gson.fromJson(j, User.class);
System.out.println(u);
}
}
Result
User [id=1, lastName=lastName, name=ignacio, school=School [id=1, name=null]]
Try with the Jackson Library. With Gson with should have not any problem, I tried with the code of #Saurabh and it work well

Jackson JSON, Immutable Classes, and Interfaces

I am playing with the Jackson examples and am having some trouble getting deserialization to work with immutable classes and interfaces.
Below is my code:
package com.art.starter.jackson_starter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
/** * Hello world! * */ public class App {
public static void main( String[] args ) throws JsonGenerationException, JsonMappingException, IOException
{
System.out.println( "Hello World!" );
AddressImpl.AddressBuilder builder = new AddressImpl.AddressBuilder();
NameImpl.Builder nameBuilder = new NameImpl.Builder();
UserImpl.Builder userBuilder = new UserImpl.Builder();
Name name = nameBuilder.first("FirstName")
.last("LastName")
.build();
Address address = builder.setCity("TestCity")
.setCountry("TestCountry")
.setState("PA")
.setStreet("TestAddress")
.setZip(123)
.build();
User user = userBuilder.address(address)
.gender(User.Gender.MALE)
.isVerified(true)
.userImage(new byte[5])
.build();
System.out.println(address);
System.out.println(name);
System.out.println(user);
StringWriter sw = new StringWriter();
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(sw, user);
System.out.println(sw);
StringReader sr = new StringReader("{\"address\":{\"state\":\"PA\",\"country\":\"TestCountry\",\"street\":\"TestAddress\",\"city\":\"TestCity\",\"zip\":123},\"verified\":true,\"gender\":\"MALE\",\"userImage\":\"AAAAAAA=\"}");
/*
This line throws the Exception
*/
User user2 = mapper.readValue(sr, UserImpl.class);
System.out.println(user2);
} }
package com.art.starter.jackson_starter;
import java.util.Arrays;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
public final class UserImpl implements User
{
private final Address address;
private final Gender gender;
private final byte[] userImage;
private final boolean isVerified;
public static class Builder
{
private Address address;
private Gender gender;
// private Name name;
private byte[] userImage;
private boolean isVerified;
public Builder address(Address address)
{
this.address = address;
return this;
}
public Builder gender(Gender gender)
{
this.gender = gender;
return this;
}
// public Builder name(Name name)
// {
// this.name = name;
// return this;
// }
public Builder userImage(byte[] userImage)
{
this.userImage = userImage;
return this;
}
public Builder isVerified(boolean isVerified)
{
this.isVerified = isVerified;
return this;
}
public UserImpl build()
{
return new UserImpl(address, gender, userImage, isVerified);
}
}
#JsonCreator
public UserImpl(#JsonProperty("address") Address address, #JsonProperty("gender") Gender gender, #JsonProperty("userImage") byte[] userImage,
#JsonProperty("verified") boolean isVerified)
{
super();
this.address = address;
this.gender = gender;
this.userImage = userImage;
this.isVerified = isVerified;
}
public Address getAddress()
{
return address;
}
public Gender getGender()
{
return gender;
}
public byte[] getUserImage()
{
return userImage;
}
public boolean isVerified()
{
return isVerified;
}
#Override
public String toString()
{
StringBuilder builder2 = new StringBuilder();
builder2.append("UserImpl [address=");
builder2.append(address);
builder2.append(", gender=");
builder2.append(gender);
builder2.append(", isVerified=");
builder2.append(isVerified);
builder2.append(", name=");
builder2.append(", userImage=");
builder2.append(Arrays.toString(userImage));
builder2.append("]");
return builder2.toString();
}
}
package com.art.starter.jackson_starter;
import org.codehaus.jackson.annotate.JsonCreator;
import org.codehaus.jackson.annotate.JsonProperty;
public final class AddressImpl implements Address
{
private final String city;
private final String country;
private final String street;
private final String state;
private final int zip;
public static class AddressBuilder
{
private String city;
private String country;
private String street;
private String state;
private int zip;
public AddressBuilder setCity(String city)
{
this.city = city;
return this;
}
public AddressBuilder setCountry(String country)
{
this.country = country;
return this;
}
public AddressBuilder setStreet(String street)
{
this.street = street;
return this;
}
public AddressBuilder setState(String state)
{
this.state = state;
return this;
}
public AddressBuilder setZip(int zip)
{
this.zip = zip;
return this;
}
public AddressImpl build()
{
return new AddressImpl(city, country, street, state, zip);
}
}
#JsonCreator
public AddressImpl(#JsonProperty("city") String city, #JsonProperty("country") String country, #JsonProperty("street") String street,
#JsonProperty("state") String state, #JsonProperty("zip") int zip)
{
this.city = city;
this.country = country;
this.street = street;
this.state = state;
this.zip = zip;
}
public String getCity()
{
return city;
}
public String getCountry()
{
return country;
}
public String getStreet()
{
return street;
}
public String getState()
{
return state;
}
public int getZip()
{
return zip;
}
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append("AddressImpl [city=");
builder.append(city);
builder.append(", country=");
builder.append(country);
builder.append(", state=");
builder.append(state);
builder.append(", street=");
builder.append(street);
builder.append(", zip=");
builder.append(zip);
builder.append("]");
return builder.toString();
}
}
The issue appears to be with Address. I get this exception:
Exception in thread "main" org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.art.starter.jackson_starter.Address, problem: abstract types can only be instantiated with additional type information
at [Source: java.io.StringReader#785f8172; line: 1, column: 2]
at org.codehaus.jackson.map.JsonMappingException.from(JsonMappingException.java:163)
at org.codehaus.jackson.map.deser.StdDeserializationContext.instantiationException(StdDeserializationContext.java:212)
at org.codehaus.jackson.map.deser.AbstractDeserializer.deserialize(AbstractDeserializer.java:97)
at org.codehaus.jackson.map.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:230)
at org.codehaus.jackson.map.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:595)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:472)
at org.codehaus.jackson.map.deser.BeanDeserializer.deserialize(BeanDeserializer.java:350)
at org.codehaus.jackson.map.ObjectMapper._readMapAndClose(ObjectMapper.java:2391)
at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1614)
at com.art.starter.jackson_starter.App.main(App.java:56)
I am sure this is because there is no way for Jackson to resolve Address which is an interface to AddressImpl which is a concrete implementation. I have been poking through the docs and have looked at a few articles regarding the #JsonDeserialize(as=AddressImpl.class),but it didn't work. So I am stumped. Has anyone ever gotten this to work, is it even supported?
It works like a champ if I replace Address with AddressImpl in the UserImpl class.
Just in case you hadn't seen it, here's a blog entry that discusses working with immutable objects and Jackson.
But you should definitely be able to use #JsonDeserialize(as=AddressImpl.class); either by adding it to Address.java interface (either directly or by using mix-ins), or by adding it to field or property. One thing to note is that for deserialization, it MUST be next to accessor you use; setter if you have one, if not, next to field. Annotations are not (yet) shared between accessors; so for example adding it to 'getter' would not work.
Jackson 1.8 also finally allows registration of abstract-to-concrete types (see http://jira.codehaus.org/browse/JACKSON-464 for more details) which might be the best option to indicate that 'AddressImpl' is to be used for 'Address'.

Categories

Resources