Jackson: Printing Values as Keys in the JSON - java

Normally what we do in Jackson to print a class as JSON object is to define getter and setter like
public class MyClass
{
private Integer a;
private Integer b;
public myClass(Integer a, Integer b)
{
this.a = a;
this.b = b;
}
#JsonProperty
public Integer getA()
{
return a;
}
#JsonProperty
public Integer getB()
{
return b;
}
public void setA(Integer a)
{
this.a = a;
}
public void setB(Integer b)
{
this.b = b;
}
}
and this will return {"a":1,"b":2}
But can I get output as {1:2} instead of what I am getting before?

In order to achieve that, you need to write own code and pass accordingly. But output you showed is standard json format which you cannot change, but definitely you can change it with code.

If you do not care that output JSON is not valid you can write custom serializer for your POJO class. It could look like this:
class MyClassJsonSerializer extends JsonSerializer<MyClass> {
#Override
public void serialize(MyClass myClass, JsonGenerator generator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
generator.writeStartObject();
generator.writeRaw(myClass.getA() + ":" + myClass.getB());
generator.writeEndObject();
}
}
Using:
#JsonSerialize(using = MyClassJsonSerializer.class)
class MyClass {
....
}
Since now, your POJO should be serialized to desired output.

Related

Builder for existing classes

I have got the following scenario in which I got four classes autogenerated (in a JAR):
Class A{
B bEl = ...;
}
Class B{
C cEl = ...;
}
Class C{
D dEl = ...;
}
Class E{
E eEl=...;
}
Setting up those objects it is quite painful and error prone. Therefore, I was wondering if there is a better way to automatically construct a builder. I am aware of Lombok but I cannot edit that code and I cannot add the #Builder annotation.
Any recommendation?
If you are not allowed to change existing classes you can extend them:
public class Existing {
String a;
String b;
public Test(String a, String b) {
this.a = a;
this.b = b;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getB() {
return b;
}
public void setB(String b) {
this.b = b;
}
}
public class ExistingBuilder extends Existing {
#Builder
public ExistingBuilder(String a, String b) {
super(a, b);
// in case super class doesn't have all arguments constructor just call setters
// setA(a);
// setB(b);
}
}
So as you can see it's doable, but super class should have getters/setters or all args constructor.

Creating JSON by combining fields from two classes

I have two classes : class A , class B
class A{
private int F1;
private String F2;
}
class B{
private int F3;
private String F4;
private String F5;
}
I want a JSON like this:
{
"F1": 123
"F2": "ABC"
"F3": 456
"F4": "CDE"
"F5": "FGH"
}
I am using springboot which creates JSON as soon as I return object from #RestController. How can I achieve the above json using these two classes.
Note :
1.) I already know that by using class A extends B , I can achieve
this but I am looking for some spring based method to achieve this
2.) Using #Embeddable in class B & then creating reference in Class A creates
additional tag B in JSON as shown :
{
"F1": 123
"F2": "ABC"
b: {
"F3": 456
"F4": "CDE"
"F5": "FGH"
}
}
How about using jackson #JsonUnwrapped?
http://fasterxml.github.io/jackson-annotations/javadoc/2.0.0/com/fasterxml/jackson/annotation/JsonUnwrapped.html
public class A{
#JsonUnwrapped
private B b;
public User getB() ...
}
Create a delegating class AB:
public final class AB {
private final A a;
private final B b;
public AB(A a, B b) {
this.a = a;
this.b = b;
}
// Delegation methods to A
public int getF1() { return this.a.getF1(); }
public String getF2() { return this.a.getF2(); }
// Delegation methods to B
public int getF3() { return this.b.getF3(); }
public String getF4() { return this.b.getF4(); }
public String getF5() { return this.b.getF5(); }
}

Java | variable value of attribute

I need to let a user of my program name an attribute of annotation, so I created fields in that class which could be managed by a user in main(), these fields should initialize a name attribute in the annotation of a getter, but Intellij IDEA tells that "Attribute value must be constant". Do you have any ideas how to do another way?
There is the code:
public class Model {
private String a;
private String b;
String nameA;
public User(String a) {
this.a = a;
}
public User(String a, String b) {
this.a = a;
this.b = b;
}
#XmlElement(name = nameA)
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
}
You can only use constant expressions in annotations.
It appears you want to map your property using a dynamic element name. For this change the type of JAXBElement<String> and use the #XmlElementRef annotation instead of #XmlElement. You can then construct your value as:
new JAXBElement(new QName(nameA), String.class, "myValue");

Getting error in a Dropwizard resource method

I am trying to study Dropwizard and so, I am trying to build a simple calculator.
This is the relevant code I wrote:
In the application class:
#Override
public void run(final CalDropWizServerDemoConfiguration configuration,
final Environment environment) {
final CalDropWizServerDemoResource resource = new CalDropWizServerDemoResource();
environment.jersey().register(resource);
final TemplateHealthCheck healthCheck = new TemplateHealthCheck();
environment.healthChecks().register("Sum", healthCheck);
}
The resource class:
#Path("/calculator")
#Produces(MediaType.APPLICATION_JSON)
public class CalDropWizServerDemoResource {
private final AtomicLong counter;
public CalDropWizServerDemoResource(){
this.counter = new AtomicLong();
}
#GET
#Timed
#Path("/sum")
public Sum calcSum(#PathParam("a") int a, #PathParam("b") int b) {
System.out.println(a);
System.out.println(b);
return new Sum(counter.incrementAndGet(), a + b);
}
}
The api (POJO of the json response) class:
public class Sum {
private long id;
private int sum;
public Sum() {
// Jackson deserialization
}
public Sum(long id, int sum) {
this.id = id;
this.sum = sum;
}
#JsonProperty
public long getId() {
return id;
}
#JsonProperty
public int getSum() {
return sum;
}
}
I don't use a configuration yaml so the configuration class is the default one.
My problem is that when I call, for example:
http://localhost:8080/calculator/sum?a=1&b=5
I get 0 as the sum.
I debugged and found out that the values of a and b in the calcSum() method are accepted as 0. Why is that?
Thanks!!
Replace #PathParam with #QueryParam and it should work fine.
#GET
#Timed
#Path("/sum")
public Sum calcSum(#QueryParam("a") int a, #QueryParam("b") int b) {
System.out.println(a);
System.out.println(b);
return new Sum(counter.incrementAndGet(), a + b);
}
If you are using #PathParams to input a and b, you need to have placeholders for them in your #Path url. It can be like this:
#Path("/sum/{a}/{b}")
The label given to each placeholder in #Path should match the value passed to corresponding #PathParam declaration.

Minimize JSON to deserialize and persist complex POJO with JPA/Hibernate

I have a fairly complex POJO that I need to deserialize from a JSON string and persist in a MySQL database. The following is a very simplified example class:
#Entity
#Table(name="a")
public class A{
private Long id;
private B b;
private C c;
private D d;
}
#ManyToOne
#JoinColumn(name="b_id")
public B getB(){
return this.b;
}
public void setB(B b){ this.b = b; }
#ManyToOne
#JoinColumn(name="c_id")
public C getC(){
return this.c;
}
public void setC(C c){ this.c = c; }
#ManyToOne
#JoinColumn(name="d_id")
public D getD(){
return this.d;
}
public void setD(D d){ this.d = d; }
Each class B, C, and D also have a number of fields and objects (some with even more required objects and fields) that can not be null according to the database schema, which I can't change. I can deserialize and persist this no problem, but the JSON required to do so is really massive. I only need to persist the deserialized A, so I really just need the _id fields from B, C, and D.
Right now my JSON is something like:
{
"id":1,
"b":{"id":2, ...},
"c":{"id":3, ...},
"d":{"id":4, ...}
}
where I have to fill in all the non-nullable database fields. What I would like to do is read a JSON string like:
{
"id":1,
"b_id":2,
"c_id":3,
"d_id":4
}
and just have Hibernate/JPA update those fields in the database. I think the real tricky part is that other classes/methods in my application will need the entire object hierarchy for reading from the database. The only time I can use just the _id fields is during the deserialization of the JSON. When this is the case, I only need to update the top-most object (A in my example) and some other trivial fields. In a perfect world, I can just throw some annotations on my class to solve this problem, but I haven't found anything capable of doing this.
Is this even possible? If so, can it be done with JPA/Jackson annotations?
Thanks!
If mapping the JSON directly to your entities is akward, I would simply make the translation from the incoming JSON string to your entities explicit via an intermediate object (call it a DTO if you will).
I also have a gut feeling that if you save associated object references (the #ManyToOnes) with only the id populated, Hibernate will in fact save the association correctly (root object here represents A):
{
"id" : 1,
"b" : {
"id" : 2
},
...
}
Make sure you don't cascade the persist operation for the b, c etc. fields.
You should create new classes which define only this fields which you want to deserialize. For example if you want to deserialize only ID your class could looks like this:
class JsonEntity {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#JsonAnySetter
public void setUnknownProperties(String name, String value) {
//do nothing
}
#Override
public String toString() {
return String.valueOf(id);
}
}
In this example annotation JsonAnySetter do the trick. Now, you have to create some class which will be simulating your A class. For example:
class DtoA extends JsonEntity {
private JsonEntity b;
private JsonEntity c;
private JsonEntity d;
public JsonEntity getB() {
return b;
}
public void setB(JsonEntity b) {
this.b = b;
}
public JsonEntity getC() {
return c;
}
public void setC(JsonEntity c) {
this.c = c;
}
public JsonEntity getD() {
return d;
}
public void setD(JsonEntity d) {
this.d = d;
}
#Override
public String toString() {
return "A [id=" + getId() + ", b=" + b + ", c=" + c + ", d=" + d + "]";
}
}
Now when we have new JSON data model we can test it. For example we can parse below JSON:
{
"id":1,
"b":{"id":2, "p1":"v1", "p2":"v2"},
"c":{"id":3, "p3":"v3", "p4":"v4", "p5":"v5"},
"d":{"id":4, "p6":"v6"}
}
Deserialization example:
ObjectMapper objectMapper = new ObjectMapper();
DtoA a = objectMapper.readValue(json, DtoA.class);
System.out.println(a);
Above program prints:
A [id=1, b=2, c=3, d=4]
Now, you have to implement toA() method in DtoA class which could look like this:
public A toA() {
A a = new A(getId());
a.setB(new B(getB().getId()));
a.setC(new C(getC().getId()));
a.setD(new D(getD().getId()));
return a;
}
Let me know whether it works for you.

Categories