I have an object stored in cache in JSON format, it is being serialized using Jackson ObjectMapper. Now I have modified that object with new fields, provided setters and getters for them. When I try to read "old" object from the cache and deserialize it into Java object, I receive Jackson exception:
Error deserializing object of type com....Person.
No suitable constructor found for type [simple type, class com....Person]: can not instantiate from JSON object (need to add/enable type information?)
How can I avoid this behavior, so that for the new fields default object values will be used?
This is how my pojo looks like after I've modified it by adding new field phone.
class Person
{
private String firstName = "";
private String lastName = "";
private int age;
private String address = "";
private Integer salary;
private String phone = "";
// default constructor required for json/smile serialization
public Person()
{
}
public Person(String firstName, String lastName, int age, String address, Integer salary, String phone)
{
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
this.salary = salary;
this.phone = phone;
}
public String getFirstName()
{
return firstName;
}
public String getLastName()
{
return lastName;
}
public int getAge()
{
return age;
}
public String getAddress()
{
return address;
}
public Integer getSalary()
{
return salary;
}
public String getPhone()
{
return phone;
}
public void setPhone(String phone)
{
this.phone = phone;
}
}
Related
I converted UUID to string (String id) and put the conversion inside a method.
I also declared other String variables such as FirstName etc and put in on an ArrayList:
Code
The code does work. But I'm confused why the string email was showing second on the list.
public class StudentController {
#Autowired
StudentService studentService = new StudentService();
#GetMapping
public List<Student> displayStudent(){
return studentService.getStudent();
}
}
public class StudentService {
Student student = new Student();
private List<Student> studentList = Arrays.asList(
new Student(student.genID(),"Elvis" , "Presley" ,"Elvis#gmail.com")
);
public List<Student> getStudent(){
return studentList;
}
}
public class Student {
UUID uuid = UUID.randomUUID();
private String id;
private String FirstName;
private String LastName;
private String email;
public Student() {}
//Method Converting UUID into string
public String genID(){
id = uuid.toString();
return id;
}
public Student(String id) {
this.id = id;
}
public Student(String id, String firstName, String lastName, String email) {
this.id = id;
FirstName = firstName;
LastName = lastName;
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return FirstName;
}
public void setFirstName(String firstName) {
FirstName = firstName;
}
public String getLastName() {
return LastName;
}
public void setLastName(String lastName) {
LastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
Expected
I expected data to be in this order
ID , FirstName , LastName , email
Actual Output JSON
JSON is an unordered collection, as specified on https://www.json.org/json-en.html , so you don't have to worry about it. It might depend on library though.
Specify the serialized order of properties
The order of properties during serialization can be defined in Jackson.
Either at class-level specifically using annotation #JsonPropertyOrder.
Or globally for your ObjectMapper using a feature:
objectMapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
Example
In your case you can achieve expected order using the annotation on your class:
#JsonPropertyOrder({'id', 'firstName', 'lastName', 'email'})
public class Student {
// body of your class
}
Or separately with an index on your fields:
public class Student {
#JsonProperty(index=10)
private String id;
// not ordered specifically
private String firstName;
private String lastName;
#JsonProperty(index=20)
private String email;
// remainder of your class
}
See also
Jackson ObjectMapper - specify serialization order of object properties
Order of JSON objects using Jackson's ObjectMapper
Jackson JSON - Using #JsonPropertyOrder annotation to define serialized properties ordering
Address class:
public class Address {
private String country;
private String county;
private String city;
private String postcode;
private String HouseNumber;
public Address(String country, String county, String city, String postcode, String HouseNumber) {
this.country = country;
this.county = county;
this.city = city;
this.postcode = postcode;
this.HouseNumber = HouseNumber;
}
public void view_adress() {
String[] address = {country, county, city, postcode, HouseNumber};
for (int i = 0; i<address.length; i++) {
System.out.println(address[i]);
}
}
public void viewHouseNumber() {
System.out.print(HouseNumber);
}
}
Person class:
public class Person {
private String firstName;
private String lastName;
private String Date_of_birth;
private String PhoneNumber;
private String[] address;
public Person (String firstName, String lastName, String Date_of_birth, String PhoneNumber, String[] address) {
this.firstName = firstName;
this.lastName = lastName;
this.Date_of_birth = Date_of_birth;
this.PhoneNumber = PhoneNumber;
this.address = address;
}
public void view_PhoneNumber() {
System.out.print(PhoneNumber);
}
}
Make use of OOP Composition.
public class Person {
//...
List<Address> addresses;
//...
}
One instance of a Person will have a 0 or more instances of Address.
Note, that in a real world scenario, you better want to retain a list of userIds in your Address class as well, because, more-than-one users, might have one, or also more-than-one addresses, which means, that that your relation must be Many-To-Many.
No less (at all) important thing it to stick with the Java Naming Conventions and name:
classes with PascalCase;
fields and method names with camelCase;
constants with ALL_CAPS_SEPARATED_WITH_UNDERSCORES.
I am using Java 8 to perform this task. I also following dependency work with JDK8 datatypes.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
<version>2.6.3</version>
</dependency>
I have a class that looks like
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Optional;
public class Person {
private String firstName;
private String lastName;
private int age;
private Optional<Address> address;
private Optional<String> phone;
private Person() {
}
public Person(String firstName, String lastName, int age) {
this(firstName, lastName, age, Optional.empty(), Optional.empty());
}
public Person(String firstName, String lastName, int age,
Optional<Address> address, Optional<String> phone) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
this.phone = phone;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public int getAge() {
return age;
}
#JsonIgnore
public Optional<Address> getAddress() {
return address;
}
#JsonIgnore
public Optional<String> getPhone() {
return phone;
}
#JsonProperty("address")
private Address getAddressForJson(){
return address.orElse(null);
}
#JsonProperty("phone")
private String getPhoneForJson() {
return phone.orElse(null);
}
}
and
public class Address {
private String street;
private String city;
private String state;
private int zip;
private String country;
public Address(String street, String city, String state, int zip, String country) {
this.street = street;
this.city = city;
this.state = state;
this.zip = zip;
this.country = country;
}
public String getStreet() {
return street;
}
public String getCity() {
return city;
}
public String getState() {
return state;
}
public int getZip() {
return zip;
}
public String getCountry() {
return country;
}
}
I write a test to write a valid Person object to a file and and read it back to a Person object. My test is
#Test
public void writeAndReadPersonAsJsonOnFile() throws Exception {
Address address = new Address("1 Infinite Loop", "Cupertino", "CA", 95014, "USA");
String phone = "1-800-My-Apple";
Person person = new Person("john", "doe", 21, Optional.of(address), Optional.of(phone));
ObjectMapper objectMapper = registerJdkModuleAndGetMapper();
File file = temporaryFolder.newFile("person.json");
objectMapper.writeValue(file, person);
assertTrue(file.exists());
assertTrue(file.length() > 0);
Person personFromFile = objectMapper.readValue(file, Person.class);
assertEquals(person, personFromFile);
}
private ObjectMapper registerJdkModuleAndGetMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());
return objectMapper;
}
The file created as part of test has following contents
{
"firstName": "john",
"lastName": "doe",
"age": 21,
"address": {
"street": "1 Infinite Loop",
"city": "Cupertino",
"state": "CA",
"zip": 95014,
"country": "USA"
},
"phone": "1-800-My-Apple"
}
But when reading back, I get personFromFile which looks like following
personFromFile = {Person#1178}
firstName = "john"
lastName = "doe"
age = 21
address = null
phone = null
as you can see, the address and phone they both are null, even though they are present in the file.
What is wrong here?
UPDATE
The codebase is https://github.com/101bits/java8-optional-json. This also contains the failing test
Try marking one of the constructors with #JsonCreator to tell Jackson which constructor to use. Note: this also requires you to mark each of the constructor's parameters with #JsonProperty
You should use the #JsonCreator annotation when you want Jackson to constructor objects with a constructor or factory method as opposed letting Jackson use setters or public (non-final) fields
Additionally, your test will not pass until you override "equals" for both Person and Address
public class Person {
private String firstName;
private String lastName;
private int age;
private Optional<Address> address;
private Optional<String> phone;
public Person(String firstName, String lastName, int age) {
this(firstName, lastName, age, Optional.empty(), Optional.empty());
}
#JsonCreator
public Person(
#JsonProperty("firstName") String firstName,
#JsonProperty("lastName") String lastName,
#JsonProperty("age") int age,
#JsonProperty("address") Optional<Address> address,
#JsonProperty("phone") Optional<String> phone) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
this.phone = phone;
}
Update: Pull Request with passing tests
As far as i have read optional does not get serialized and hence, while deserializing you wont get the value if you are using default java serialization. However, if you are using your serialization, it should be fine.
Refer this link for more details:
Why java.util.Optional is not Serializable, how to serialize the object with such fields
Please help me find errors from this code. I'm still new and I don't know if this is correct or not.
I do have one error.
This is the error:
constructor Person in class Person cannot be applied to given types;
super();
^
required: String,String,String
found: no arguments
reason: actual and formal argument lists differ in length
This is my code:
import java.util.*;
public class Person {
//Data fields
private String lastName;
private String middleInitial;
private String firstName;
//Constructors
public Person(String lastName, String middleInitial, String firstName) {
this.lastName = lastName;
this.middleInitial = middleInitial;
this.lastName = lastName;
}
//Accessor methods
public String getlastName() {
return lastName;
}
public String getmiddleInitial() {
return middleInitial;
}
public String getfirstName() {
return firstName;
}
//Mutator methods
public void setlastName(String lastName) {
lastName = lastName;
}
public void setmiddleInitial(String middleInitial) {
middleInitial = middleInitial;
}
public void setfirstName(String firstName) {
firstName = firstName;
}
public String getName() {
String studentName = this.lastName + ", " + this.firstName +
this.middleInitial + ".";
return studentName;
}
} //end Person class
class Address {
//Data fields
private String streetName;
private int zipCode;
private String state;
private String country;
//Constructors
public Address(String streetName, int zipCode, String state,
String country) {
this.streetName = streetName;
this.zipCode = zipCode;
this.state = state;
this.country = country;
}
//Accessor methods
public String getstreetName() {
return streetName;
}
public int getzipCode() {
return zipCode;
}
public String getstate() {
return state;
}
public String getcountry() {
return country;
}
//Mutator methods
public void setstreetName(String streetName) {
streetName = streetName;
}
public void setzipCode(int zipCode) {
zipCode = zipCode;
//Integer.toString(zipCode);
}
public void setstate(String state) {
state = state;
}
public void setcountry(String country) {
country = country;
}
public String getAddress() {
String studentAddress = streetName + "\n" + state + ", " + country +
"\n" + zipCode;
return studentAddress;
}
} //end Address class
class Student extends Person {
private String dateOfBirth;
//Constructors
public Student (String studentName, String dateOfBirth) {
super();
dateOfBirth = dateOfBirth;
}
//Accessor methods
public String getdateOfBirth() {
return dateOfBirth;
}
//Mutator methods
public void setdateOfBirth() {
this.dateOfBirth = dateOfBirth;
}
public String toString() {
return ("Date of Birth: " + dateOfBirth);
}
} //end Student subclass
Edited: If I do so for both the Person and Address class. I can only have three-arg constructors. How can I call a one-arg constructor?
For example, I have
public Student (String firstName, String lastName, String middleInitial, String dateOfBirth) {
super(firstName, lastName, middleInitial); and
public Student (String streetName, String state, String country) {
super(streetName, state, country);
How can I get zipcode separately?
Class Person has a constructor, therefore the default no-arg constructor is not created for you. Therefore you can't call super() in Student's constructor, you have to call super(lastName, middleInitial, firstName);.
Or you could create a new Person no-arg constuctor.
Try this
In student class
public Student ( String lastName, String middleInitial, String firstName,String studentName, String dateOfBirth) {
super( lastName, middleInitial,firstName);
this.dateOfBirth = dateOfBirth;
}
Or
In Person class create no arg consructor. Eg:
public Person(){}
Person Class has a constructor with arguments. So default constructor will not be created. So you have to pass 3 String parameters in super(3 String parameters) or you have to create a constructor which does not take any parameter in person class.
I need to delete certain lines from a file, the file is a list of contacts that I read in from a file into my GUI. When I get to the contact I want to delete, my program should delete the contact from the file. I've tried to do this, but it is not working.
Here is the code I'm currently using:
String temp=txtname.getText();
for (Contact Contact:contacts)
{
if (temp.equals(Contact.getname()));
{
txtname.setText("");
txtsurname.setText("");
txtphone.setText("");
txtmobile.setText("");
txtaddress.setText("");
txtpostcode.setText("");
contacts.remove(Contact);
contacts.remove(Contact);
contacts.remove(Contact);
contacts.remove(Contact);
contacts.remove(Contact);
contacts.remove(Contact);
}
}
My contact class is:
public class Contact {
static void add(String text) {
}
public String name;
public String surname;
public String phone;
public String mobile;
public String address;
public String postcode;
public Contact(){}
public Contact(String name, String surname, String phone,
String mobile, String address, String postcode)
{
this.name = name;
this.surname = surname;
this.phone = phone;
this.mobile = mobile;
this.address = address;
this.postcode = postcode;
}
public String getname()
{
return this.name;
}
public String getsurname()
{
return this.surname;
}
public String getphone()
{
return this.phone;
}
public String getmobile()
{
return this.mobile;
}
public String getaddress()
{
return this.address;
}
public String getpostcode()
{
return this.postcode;
}
public void setname(String name)
{
this.name = name;
}
public void setsurname(String surname)
{
this.surname = surname;
}
public void setphone(String phone)
{
this.phone = phone;
}
public void setmobile(String mobile)
{
this.mobile = mobile;
}
public void setaddress(String address)
{
this.address = address;
}
public void setpostcode(String postcode)
{
this.postcode = postcode;
}
}
I'm guessing it deletes it from the arraylist, but I'm not sure how the program knows what to delete from the file.
Thanks.
Modifying the internal list doesn't change the file. There is no automatic way to synchronize the two. You have to save the array back to the file to update it.
There is no way to delete anything from the middle of the file.
The only way is to rewrite the file every time something should be changed.
you can use a random access file but it seems like an over kill for this task.
the best way to do it is to have the remove function write the whole file back to the disk.
If you have no specific format for the file, then I suggest you to use the default serialization and serialize the contacts list. Like this,
//To serialize
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File("D:/newfile.txt")));
out.writeObject(contacts);
//To deserialize
ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File("d:/newFile.txt")));
contacts = (ArrayList<Contact>)in.readObject();
You first have to implement the Serializable interface in your Contact class.
Or if you need more control over the file format use the standard XML classes or JSON libraries.
Files can be rewrited, or appended to. In you case you'll have to rewrite it. There are other ways, but they would be an overkill here.
String temp = txtname.getText();
for (Contact contact : contacts) {
if (temp.equals(contact.getname())) {
contacts.remove(contact);
break;
}
}
Fixed a lot of general problems with your code
public class Contact {
private String name;
private String surname;
private String phone;
private String mobile;
private String address;
private String postcode;
public Contact(String name, String surname, String phone, String mobile, String address, String postcode) {
this.name = name;
this.surname = surname;
this.phone = phone;
this.mobile = mobile;
this.address = address;
this.postcode = postcode;
}
public String getName() {
return this.name;
}
public String getSurname() {
return this.surname;
}
public String getPhone() {
return this.phone;
}
public String getMobile() {
return this.mobile;
}
public String getAddress() {
return this.address;
}
public String getPostcode() {
return this.postcode;
}
}