Jackson serialize Java Object to include class name - java

I have these objects:
public class Person {
private String name;
private String age;
private List<Job> list = new ArrayList<>();
//getters and setters
}
public class Job {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
using Jackson in main:
ObjectMapper mapper = new ObjectMapper();
mapper.activateDefaultTypingAsProperty( new DefaultBaseTypeLimitingValidator(),
ObjectMapper.DefaultTyping.NON_FINAL, "class");
String json2 = mapper.writeValueAsString( person );
json2:
{
"class":"com.johnson.testing.Person",
"name":"Brandon Johnson","age":50,
"list":["java.util.ArrayList",
[{"class":"com.johnson.testing2.Job",
"title":"work"}]]
}
I want the "class" key but I do not want the "java.util.ArrayList" field. I
am using Jackson to serialize nested objects that are in libraries. I do not have
access to the Java Objects to annotate the classes. How can I just have the class
with path added to the many nested objects and not the other stuff?

Related

Understanding immutability with builder pattern and Cucumber-JVM and Guice

I'm using cucumber-jvm and cucumber-guice in my project for test automation. I have a POJO with builder pattern:
class Book {
String title;
String author;
String date;
// builder, getter, setter
}
Then, in cucumber test I need to share the state of the Book object among two steps:
class BookSteps {
#Inject
Book book;
void firstStep() {
buildBook();
}
void secondStep() {
buildBook().setDate("2019-09-04");
}
Book buildBook() {
return book = Book().BookBuilder().title("Foo").author("Bar").build();
}
}
So, as I understood the builder pattern correctly, it creates an immutable object of book. But, why then I'm able to modify its state in secondStep() method by calling a setDate() on it and eventually modifying it?
You are able to modify state because builder pattern is not implemented correctly. In builder pattern:
You don't provide mutators(or setters) in your class. Only way to set properties of your class is via builder. Properties can be set only , either via builder constructor or via the mutator methods of builder. Usually for mandatory fields , you initialize them via builder constructor and then set other optional properties using builder setter methods.
So your Book class with builder patter should look like as below :
public class Book {
private String title;
private String author;
private String date;
private Book(Builder builder) {
title = builder.title;
author = builder.author;
date = builder.date;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public String getDate() {
return date;
}
public static final class Builder {
private String title;
private String author;
private String date;
public Builder() {
}
public Builder title(String val) {
title = val;
return this;
}
public Builder author(String val) {
author = val;
return this;
}
public Builder date(String val) {
date = val;
return this;
}
public Book build() {
return new Book(this);
}
}
}
And below is the test class:
public class TestBookBuilder {
public static void main(String[] args) {
Book book = new Book.Builder().title("Book title").author("Book Author").date("25-01-2020").build();
}
}
Now instance of Book class is immutable.
Hope it was helpful.

How can JAXB be used to map attributes on an element to fields of an intrinsic property of a POJO?

Let's pretend I have the following XML:
<company name="Sun" country="Atlantis" state="Syracuse" city="Troy">
</company>
With JAXB, and without using third-party extensions such as EclipseLink's #XmlPath, is there a way to unmarshall it into the following POJO structure:
#XmlRootElement
public class Company {
private String name;
private Address address;
// getters and setters
}
public class Address {
private String country;
private String state;
private String city;
// getters and setters
}
company.getAddress().getCountry(); // Atlantis
This particular scenario can be handled using an XmlAdapter:
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.time.format.DateTimeFormatter;
public class CompanyAdapter extends XmlAdapter<CompantType, Company> {
#Override
public CompanyType marshal(Company in) throws Exception {
CompanyType out = new CompanyType();
out.setName(in.getName());
out.setCountry(in.getAddress().getCountry());
// ...
return out;
}
#Override
public Company unmarshall(CompanyType in) throws Exception {
Company out = new Company();
out.setName(in.getName());
Address add = new Address();
add.setCountry(in.getCountry());
out.setAddress(add);
// ...
return out;
}
}

jackson object property into primitive

I have the follow json.
{
foo:{
id:1
},
name:'Albert',
age: 32
}
How can I deserialize to Java Pojo
public class User {
private int fooId;
private String name;
private int age;
}
This is what you need to deserialize, using the JsonProperty annotations in your constructor.
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
public class User {
private int fooId;
private String name;
private int age;
public int getFooId() {
return fooId;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public User(#JsonProperty("age") Integer age, #JsonProperty("name") String name,
#JsonProperty("foo") JsonNode foo) {
this.age = age;
this.name = name;
this.fooId = foo.path("id").asInt();
}
public static void main(String[] args) {
ObjectMapper objectMapper = new ObjectMapper();
String json = "{\"foo\":{\"id\":1}, \"name\":\"Albert\", \"age\": 32}" ;
try {
User user = objectMapper.readValue(json, User.class);
System.out.print("User fooId: " + user.getFooId());
} catch (IOException e) {
e.printStackTrace();
}
}
}
Output:
User fooId: 1
Hope it helps,
Jose Luis
You can do one of the following:
Create a concrete type representing Foo:
public class Foo {
private int id;
...
}
Then in User you would have:
public class User {
private Foo foo;
...
}
Use a Map<String, Integer>:
public class User {
private Map<String, Integer> foo;
...
}
If other callers are really expecting you to have a getFooId and a setFooId, you can still provide these methods and then either delegate to Foo or the Map depending on the option you choose. Just make sure that you annotate these with #JsonIgnore since they aren't real properties.
You can use a very helpful gson google API.
First of all, create these two classes:
User class:
public class User{
Foo foo;
String name;
int age;
//getters and setters
}
Foo class:
public class Foo{
int id;
//getters and setters
}
If you have a example.json file then deserialize it as follow
Gson gson = new Gson();
User data = gson.fromJson(new BufferedReader(new FileReader(
"example.json")), new TypeToken<User>() {
}.getType());
If you have a exampleJson String then deserialize it as follow
Gson gson = new Gson();
User data = gson.fromJson(exampleJson, User.class);

GSON parsing to object

I will be recieving JSON strings in the following format:
{ "type":"groups", "groups":[ {"group":"NAME"}, ...] }
How would one form an object so that the following would work.
MyClass p = gson.fromJson(jsonString, MyClass.class);
The part I'm stuck it is "{"group":"NAME"}". Would this be fixed by saving objects inside the an array? Example.
public class MyClass {
private String type;
private List<MyOtherClass> groups = new ArrayList<MyOtherClass>();
//getter and setter methods
}
Answer: Nesting objects in each other doh! Thanks you guys! Crystal clear now :D
public class MyOtherClass {
private String group;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
#Override
public String toString() {
return "group: "+group;
}
}
First you need a POJO for the group:
public class MyOtherClass {
#Expose
private String group;
public String getGroup() {
return group;
}
public void setGroup(String group) {
this.group = group;
}
}
Next you need one for your 'MyClass', which would look like this:
public class MyClass {
#Expose
private String type;
#Expose
private List<MyOtherClass> groups = new ArrayList<MyOtherClass>();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Group> getGroups() {
return groups;
}
public void setGroups(List<Group> groups) {
this.groups = groups;
}
}
Hope this helps.
At first glance, this looks fine, assuming MyOtherClass has a field called group that holds a String. What do you mean by "the part I'm stuck [on]"? Perhaps you could post the stack trace you're seeing, a broader description of what you're trying to do, or best of all a SSCCE?
When using GSON, I find it easiest to implement the class structure I need, then let GSON generate the JSON data from that. You certainly can go the other way (design class structure based on JSON data), but I think it's more confusing if you don't understand what GSON is trying to do.
Some pseduo-code:
Class MyClass
String type
List<MyOtherClass> groups
Class MyOtherClass
String group
Looking at this we can easily see the JSON that will hold our serialized object will look like so:
{
type: "...",
groups: [
{ group: "..." },
...
]
}

Which Java ORM framework support polymorphism of document in MongoDB?

I'm trying to use MongoDB to store a series of documents. These document share some standard attributes while it has several variation. We implement the POJO with an inheritance. The base class is Document, while it has several sub-classes such as Invoice and Orders, which has several additional fields when compared with Document class.
class Document {
DocTypeEnum type;
String title;
}
class Invoice extends Document{
Date dueDate;
}
class Order extends Document{
List<LineItems> items;
}
Is there an ORM framework support query the collection and return a list of mixed objects (invoice, order, basic document, etc) according to its type field?
List<Document> results = DocCollection.find(...);
Thanks a lot!
Morhia supports polymorphism even without requiring a type enum or anything. It stores the actual instance classname along with the rest of the data. Have a look at the #Polymorphic annotation.
You can use just any ORM that supports the desired database dialect.
The hibernate framework has Object/grid Mapper (OGM) subproject that does just this.
BuguMongo?
http://code.google.com/p/bugumongo
Another option is to use Jongo which delegates polymorphic handling to Jackson. I've wrote a blog post with some examples and you can find the full code base on GitHub.
In your specific scenario, your solution with Jackson will look like this:
public enum DocTypeEnum {
INVOICE(Constants.INVOICE), ORDER(Constants.ORDER);
DocTypeEnum(String docTypeString) {
}
public static class Constants {
public static final String INVOICE = "INVOICE";
public static final String ORDER = "ORDER";
}
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY,
property = Document.TYPE)
#JsonSubTypes({
#JsonSubTypes.Type(value = Invoice.class, name = DocTypeEnum.Constants.INVOICE),
#JsonSubTypes.Type(value = Order.class, name = DocTypeEnum.Constants.ORDER)
})
public class Document {
public static final String TYPE = "type";
public static final String TITLE = "title";
private final DocTypeEnum type;
private final String title;
public Document(DocTypeEnum type, String title) {
this.type = type;
this.title = title;
}
#JsonProperty(TYPE)
public DocTypeEnum getType() {
return type;
}
#JsonProperty(TITLE)
public String getTitle() {
return title;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class Invoice extends Document {
public static final String DUE_DATE = "due_date";
private final Date dueDate;
public Invoice(String title, Date dueDate) {
super(DocTypeEnum.INVOICE, title);
this.dueDate = dueDate;
}
#JsonProperty(DUE_DATE)
public Date getDueDate() {
return dueDate;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
public class Order extends Document {
public static final String ITEMS = "items";
private final List<LineItems> items;
public Order(String title, List<LineItems> items) {
super(DocTypeEnum.ORDER, title);
this.items = items;
}
#JsonProperty(ITEMS)
public List<LineItems> getItems() {
return items;
}
}

Categories