Serialization of non-primitives using Jackson - java

I am trying to serialize a JSON object using Jackson and save into a mysql database using hibernate. All fields of my POJO class are able to be serialized except for any field that isn't a primitive.
public class Teacher {
private Set<Student> students;
private int id;
// getters and setters
}
In this case it would fail on students, creating an infinite recursive loop through the reference chain. I can stop it with #JsonIgnoreProperty but I want this field to be serialized. I am serializing my object like so:
ObjectMapper mapper = new ObjectMapper();
Teacher myTeacher = new Teacher();
mapper.writeValueAsString(teacher);
The only workaround I can think of is appending a string to the end of teacher while still ignoring the property but I am not sure if I will be able to read students as a JsonNode from the tree if I do this.

A way around this would be to use a pure Array or an ArrayList which are serialized fine with Jackson.
For example, I can serialize a class with all these parameters :
public class Map{
private short [][] someMapType;
private short [][] someOtherMap;
private ArrayList<Mill> someMills, otherMills;
private ArrayList<OtherPOJO> myPOJOList;
private String action = "myDefaultAction";
...
}
Where Mill and OtherPOJO are class with not much more than a couple of arrays and other primitives : pure POJOs.
It works fine both ways with Jackson and MongoJack (jackson serializer for MongoDb).
If you can't get away from the set than you have to understand properly what is the fundamental data structure in a set. This should help but you are probably already aware of that.
A way to work around this structure limitation would be to create non-dumb getters and setters. The main disadvantage behind this method is that you run one more for loop over all your elements every time you serialize or de-serialize. This might slightly reduce performance.
The getter is fairly simple :
public Student[] getStudents(){
return this.students.toArray();
}
And the setter is also pretty trivial :
public void setStudents(Student[] students){
this.students = new Set<Student>(); // Or anything that builds the right Set for you
for(int i = 0; i < students.length; i++){
this.students.add(students[i]);
}
}
I wrote it quickly, let me know if there is any bug.
I hope it helps!

Found a decent workaround:
#JsonIgnore
public Set<Student> getStudents() {
return students;
}
#JsonProperty("Students")
public String getStudentsForJson() {
String[] studentNames = new String[this.students.size()];
int i = 0;
for(Student student : this.students) {
studentNames[i] = student.getName();
i++;
}
return StringUtils.join(studentNames, ", ");
}
This saves all student names as one string which I'm able to easily serialize and deserialize as a field.

Related

Java ArrayList<String> form comma-separated strings in square brackets

I am calling an end-point and I get the request body looking like this.
{"page":6,"name":"Fly","myList":["123","657","983","112"]}
So I am Creating an org.json.JSONObject like JSONObject repsonseObj = new JSONObject(repsonse.getBody());
I can now do
(int)repsonseObj.get("page");
(String)repsonseObj.get("name");
I am trying to covert "myList" into a Java ArrayList of String.
I can pull it out using a JSONArray object and use a for-loop to populate an instance of ArrayList.
Also tried something like this below using an ObjectMapper for jackson.databind
myListVar = mapper.readValue((String)repsonse.getString("myList"), new TypeReference<ArrayList<String>>(){});
//That gives an error saying "myList" is not a string. Surely I am missing something here.
I want to keep the sequence of the numbers.
Is there a more elegant solution for this? I tried looking at the StringUtils, ArrayUtils from java.lang. I just can't seem to put my finger on a better solution. Can someone point me in the right direction, thanks.
Actually why don't you parse the whole object instead of just the list?
public class MyClass {
private Integer page;
private String name;
private List<String> myList;
//getters, setters
}
And this parses the JSON to a variable
MyClass myClass = objectMapper.readValue("{\"page\":6,\"name\":\"Fly\",\"myList\":[\"123\",\"657\",\"983\",\"112\"]}", MyClass.class);
EDIT:
As it turned out in the comments you need your properties in the JSON to have different names than in your class. This can be easily achieved by annotating the class properties with #JsonProperty as #Dragos Ionut suggested. Just do the following
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class MyClass {
#JsonProperty("page")
private Integer otherNameThanPage;
#JsonProperty("name")
private String otherNameThanName;
#JsonProperty("myList")
private List<String> otherNameThanMyList;
//getters, setters
}

Dynamic Jackson fields filtering

I'm trying to implement wrapper class, that will be serialized by Jackson with omitting fields depending on runtime configuration:
class Partial<T> {
T value;
List<String> fieldsToSkip;
}
This
class Example{int a=1; int b=2; int c=3;}
new Partial(new Example(), asList("b"));
suppose to be serialized to {"a":1, "c":3}.
#JsonUnwrapped with #JsonFilter seems to be right approach here. The problem is that the filter works on value field level, where there's no access to host Partial instance.
What is the best way to implement such thing?
You can create at run time an ObjectWriter with a filter also defined at run time and use it to write the value in Partial:
SimpleBeanPropertyFilter filter =
SimpleBeanPropertyFilter.serializeAllExcept(new HashSet<>(partial.fieldsToSkip));
FilterProvider fp = new SimpleFilterProvider().addFilter("exampleFilter", filter);
String text = objectMapper.writer(fp).writeValueAsString(partial.value);
and tell Jackson that this filter should be applied to Example class:
#JsonFilter("exampleFilter")
class Example{int a=1; int b=2; int c=3;}
You may want to change fieldsToSkip to a Set<String>

Invoking bean-style getters based on matching entries from a property file

I'm looking for an automated way to read a property file and invoke corresponding setters on a Java object.
#property file
person.firstName=firstName
person.age=11
person.address.cityName=cityName
...
Please bear with the following example object model.
public class Person{
// below have getters & setters
private String firstName;
private int age;
private Address = new Address(); // not null
}
public class Address{
//below have setters and getters
private cityName;
...
}
What I'm looking for
Person personObj = new Person();
MagicTransformer mt = new MagicTransformer();
mt.doMagic("filePath", personObj);
//now the personObj has all the properties set from the file.
It'd be great if the framework is clever about transforming standard types: primitives, strings, enums, lists (primitives & strings), maps (primitives & strings)
I explored before posting. Any search related to reading 'property files' had returned Spring's property placeholder, etc. I might have missed a resource. It'd be great if you could name a couple of frameworks.
Don't know about a single lib to do that all but it should be fairly trivial to:
read the property file into a java Map (this should be fairly straightforward?)
use the above and convert it to your POJO using either http://commons.apache.org/proper/commons-beanutils/ or using https://github.com/FasterXML/jackson (usin ObjectMapper)
How about storing the data in xm; and using xmlbeans
A simple example is
File xmlFile = new File("c:\employees.xml");
// Bind the instance to the generated XMLBeans types.
EmployeesDocument empDoc =
EmployeesDocument.Factory.parse(xmlFile);
// Get and print pieces of the XML instance.
Employees emps = empDoc.getEmployees();
Employee[] empArray = emps.getEmployeeArray();
for (int i = 0; i < empArray.length; i++)
{
System.out.println(empArray[i]);
}

Benefits of factoring a new class vs string values for elements in a Set

This is more of a design question with implications for code simplicity vs. performance.
Lets say you want to make sure a set of values for a given user id are the same between two systems. The example here is to check that a student id has the same number of course enrollments in System A and System B.
For this we create:
List<String> studentList = new ArrayList<String>();
Set<String> sysAEnrollments = new HashSet<String>();
Set<String> sysBEnrollments = new HashSet<String>();
private Map<String, String> badEnrollList = new HashMap<String, String>();
And fill them appropriately, given a list of student ids(studentList):
studentList = getCurrentStudentList();
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.containsAll(sysBEnrollments)){
badEnrollList.put(id, getBadEnrollmentsById(id, sysAEnrollments, sysBEnrollments));
}
}
Question: What should the method 'getBadEnrollmentsById' return?
Either a concatenated string with enough meaning so it can just be printed out.
Or have a new object, for example another collection with the list of course ids that could be used for further processing but harder to use for printed output.
Is it worth designing thoroughly all expected objects or replace some of them with concatenated strings for clarity and performance?
NOTES:
System A is preferred as the authoritative source
Output from getBadEnrollmentsById should have all courses and flag those missing in system B.
PROPOSED SOLUTION: (2012-SEP-14)
EDIT (2012-SEP-17): Updated the Course class to include hashCode and equals
As suggested by user351721 I continued modelling the remaining objects that match the expected results/requirements.
Slight changes made a big difference and allowed me to go over this design flaw and finish with the implementation.
The revised collections are:
List<String> studentList = new ArrayList<String>();
Enrollment sysAEnrollments;
Enrollment sysBEnrollments;
Map<String, List<String>> badEnrollList = new HashMap<String, List<String>>();
And we populate the Enrollments:
for (String id : studentList){
sysAEnrollments = getSysAEnrollments(id);
sysBEnrollments = getSysBEnrollments(id);
if (!sysAEnrollments.getCourses().containsAll(sysBEnrollments.getCourses())){
List<String> missingCourses = getProblemEnrollmentListById(id, sysAEnrollments, sysBEnrollments);
badEnrollList.put(id, missingCourses);
}
}
So for now the output can be printed from badEnrollList by getting at each ArrayList and printing the course names. A course name with a * will mean that it's missing in sysB.
The Enrollment class looks like this:
public class Enrollment {
private Set<Course> courses = new HashSet<Course>();
public void setCourses(Set<Course> courses){
this.courses = courses;
}
public Set<Course> getCourses(){
return this.courses;
}
}
And the Course class ended up like this:
public class Course {
private String id;
private String name;
public String getId() {
return id;
}
public void setId(final String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
// Must override hashCode() and equals()
#Override
public boolean equals(Object o){
if (o == this)
return true;
if (!(o instanceof Course))
return false;
Course c = (Course) o;
return c.id.equals(this.id) && c.name.equals(this.name);
}
#Override
public int hashCode(){
// Magic numbers as shown on Joshua Bloch's book "Effective Java" 2nd Edition, p.48
int result = 17;
result = 31 * this.id.hashCode();
result = 31 * this.name.hashCode();
return result;
}
}
The changes might look subtle but the important clue is that Enrollments are not a collection of strings, Enrollments are a collection of Courses AND each Course has a name and a availability property. They don't seem to do much but by using them I am defining the objects that I'm working with and documenting how these classes can be reused in the future.
"Growing Object-Oriented Software, Guided by Tests" addresses this question: chapter 7, "Value Types". Worth reading. An excerpt:
The more code we write, the more we’re convinced that we should define types to represent value concepts in the domain, even if they don’t do much. It helps to create a consistent domain model that is more self-explanatory. If we create, for example, an Item type in a system, instead of just using String, we can f ind all the code that’s relevant for a change without having to chase through the method calls
concatenated strings
would mean you have to define a pattern and corresponding set of valid strings and implement validation and translation to entity classes. Providing an interface or class would make it easier to update your code in a year or so, not to mention other programmers that might work with your application. Why not store student, enrollment or course objects in badEnrollList? How do these objects look like and what do you want to do with them?
In general: Yes, designing thoroughly all expected objects is worth it.
I feel that a collection, such as List<String> would be a desirable return value. This allows you to more efficiently capture multiple discrepancies between the two sets, and process the missing courses in your second object more intuitively. Printing the list wouldn't be that hard, either - depending on how you wished to convey the information.
It's also worth mentioning that the .equals() method for Set is a cleaner and more intuitive way to ensure equivalence between two sets.
Instead of using all these sets and maps, I'd use Plain Old Java Objects (POJOs) that reflect the actual business objects in question. From what you've indicated, you have Students who have an id of some sort, and who are enrolled in classes on System A and on System B. I would build up a set of Student objects defined like so:
public class Student {
private String id;
private List<String> enrollmentsA;
private List<String> enrollmentsB;
// appropriate getters and setters
}
Depending on if you want to do anything else with Classes, it may even be preferable to create some form of EnrolledClass object to represent that too.
Within the students class, I'd then have a method that would determine the "bad" enrollments. If all that you want to do with this data is generate an email message, it may even be as simple as a String:
public String getBadEnrollmentsMessage() {
List<String> enrolledBoth = getCommonEnrollments();
List<String> enrolledOnlyA = getAOnlyEnrollments();
List<String> enrolledOnlyB = getBOnlyEnrollments();
StringBuilder output;
// format the contents of the above lists into output
// format should be however you want it in the email.
return output.toString();
}
Then you could have a map of Students to email enrollments messages:
HashMap<Student, String> studentEmails;
for (Student s : allStudents) {
studentEmails.put(s, s.getBadEnrollmentsMessage());
}
Of course, if you have a method like getBadEnrollmentsMessage(), I'm not even sure you need the Map of students and strings in the first place. Frankly you could just create a sendEnrollmentEmail method, pass in a Student, and extract the message via getBadEnrollmentsMessage() right there.

XStream Alias of List root elements

I want to be able to alias the root list element depending upon what type of objects are contained in the list. For example, this is my current output:
<list>
<coin>Gold</coin>
<coin>Silver</coin>
<coin>Bronze</coin>
</list>
And this is what I want it to look like:
<coins>
<coin>Gold</coin>
<coin>Silver</coin>
<coin>Bronze</coin>
</coins>
I can do this at a global level by saying all lists should be aliased to coins, but I have a lot of different lists and this won't work. Any ideas on how to do this? Seems like it should be simple, but of course, it isn't.
EDIT: I should specify, I am trying to serialize objects to xml. I am using Spring 3 MVC as my web framework.
Let's say you have a Coin class with a type attribute, as follows:
#XStreamAlias("coin")
public class Coin {
String type;
}
And you have a Coins class that constains a List of Coin:
#XStreamAlias("coins")
public class Coins{
#XStreamImplicit
List<Coin> coins = new ArrayList<Coin>();
}
Pay attention to the annotations. The List is Implicit and the Coins class will be shown as "coins".
The output will be:
<coins>
<coin>
<type>Gold</type>
</coin>
<coin>
<type>Silver</type>
</coin>
<coin>
<type>Bronze</type>
</coin>
</coins>
It's not the same you asked for, but there is a reason.
At first, coin have only one attribute, but we are not sure if all objects you want to show do have only one attribute too. So, we need to tell which object attribute we are talking about.
You can also show the Coin attributes as XML Attributes, not fields. As follows:
#XStreamAlias("coin")
public class Coin {
#XStreamAsAttribute
String type;
Coin(String type) {
this.type = type;
}
}
Here is the output:
<coins>
<coin type="Gold"/>
<coin type="Silver"/>
<coin type="Bronze"/>
</coins>
Hope it helps.
This isn't an ideal solution, as it requires a separate wrapper class, but you could do something like this:
public class CoinResponse {
private List<Coin> coinList;
public CoinResponse(List<Coin> coinList) {
this.coinList = coinList;
}
public List<Coin> getCoins() {
return this.coinList;
}
}
And here's the ugly part:
List<Coin> coins = Arrays.asList( new Coin(), new Coin(), new Coin());
CoinResponse response = new CoinResponse(coins);
XStream xstream = new XStream();
xstream.alias( "coins", CoinResponse.class );
xstream.addImplicitCollection( CoinResponse.class, "coinList" );
System.out.println(xstream.toXML(response));
Basically, this is telling Xstream to use "coins" when converting the CoinResponse, and then don't use any name at all for the list itself.
#XStreamAlias("coins")
public class Coins {
#XStreamImplicit(itemFieldName="coin")
List<String> coins = new ArrayList<String>();
}

Categories

Resources