Jackson: having 2 different representations of a class - java

I have a simple User class:
public class User {
private long id;
private String username;
private String password;
private String someCommonData;
private String someAdminData;
}
I would like to have different representations of that User in json.
A version for normal users:
{"username":"myName", "someCommonData":"bla"}
and a representation for adminUsers:
{"id":1, "username":"myName", "someCommonData":"bla", "someAdminData":"don't show this to the user!"}
When I use #JsonIgnore then it is always ignored but I would like to have conditional ignore.
The only solution that would work so far is to have two different classes. Isn't there a more beautiful solution?

Take a look at #JsonView
public class User {
#JsonView({Admin.class})
private long id;
#JsonView({Basic.class})
private String username;
#JsonIgnore
private String password;
#JsonView({Basic.class})
private String someCommonData;
#JsonView({Admin.class})
private String someAdminData;
static class Basic {
}
static class Admin extends Basic {
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
User user = new User();
user.id = 1L;
user.username = "admin";
user.password = "nimda";
user.someCommonData = "common-data";
user.someAdminData = "admin-data";
ObjectWriter writer = mapper.writerWithDefaultPrettyPrinter();
System.out.println(writer.withView(Basic.class).writeValueAsString(user));
System.out.println(writer.withView(Admin.class).writeValueAsString(user));
}
}
output of main:
{
"username" : "admin",
"someCommonData" : "common-data"
}
{
"id" : 1,
"username" : "admin",
"someCommonData" : "common-data",
"someAdminData" : "admin-data"
}
This blog explains the basics: http://www.baeldung.com/jackson-json-view-annotation

The best and easest aproach I can figure out is to use two classses. Sorry.
But it looks for me like a better design when you do this:
public class User {
private long id;
private String username;
private String password;
private String someCommonData;
}
public class Admin extends User {
private String someAdminData;
}

Related

How to combine data from two reactive streams in project reactor?

I started using Project Reactor recently and I can't work out how to work with nested streams. I want to update data of outer Mono with some data of inner Mono.
#GetMapping("/search")
public Mono<Github> combineGithubData() {
WebClient client = WebClient.create("https://api.github.com");
Mono<Github> data = client.get().uri(URI.create("https://api.github.com/users/autocorrectoff")).retrieve().bodyToMono(Github.class);
data = data.map(s -> {
client.get().uri(URI.create("https://api.github.com/users/Kukilej")).retrieve().bodyToMono(Github.class).map(m -> {
s.setXXX(m.getName());
return m;
});
return s;
});
return data;
}
The field XXX is always returned as null, although I have set it to a value from inner Mono. I'm pretty sure this would work in RxJs. How do I make this work with Project Reactor?
edit:
the code of the Github class
import lombok.*;
#Getter #Setter
#Builder
#ToString
#NoArgsConstructor
#AllArgsConstructor
public class Github {
private String login;
private int id;
private String node_id;
private String avatar_url;
private String gravatar_id;
private String url;
private String html_url;
private String followers_url;
private String following_url;
private String gists_url;
private String starred_url;
private String subscriptions_url;
private String organizations_url;
private String repos_url;
private String events_url;
private String received_events_url;
private String type;
private boolean site_admin;
private String name;
private String company;
private String blog;
private String location;
private String email;
private String hireable;
private String bio;
private int public_repos;
private int public_gists;
private int followers;
private int following;
private String created_at;
private String updated_at;
private String XXX;
}
Your inner stream is not getting subscribed to. Either us flatMap, or better yet, use zip:
data
.zipWith(client.get().uri(...).retrieve().bodyToMono(Github.class))
.map(tuple2 -> {
//update tuple2.getT1() with m.getName() and return the updated tuple
return tuple2.mapT1(tuple2.getT1().setXXX(tuple2.getT2().getName()));
})
.map(tuple2 -> tuple2.getT1() //updated s);
zipWith() subscribes to the inner stream.

How can I convert a Jackson DTO into a JsonSchema?

I'm trying to mimic Spring Data REST's APIs in cases where SDR isn't a good fit, such as a login or password reset route. I have this DTO
public class PasswordCredential implements
AuthenticationProvider<UsernamePasswordAuthenticationToken> {
#Email
#NotNull
#NotEmpty
private final String user;
#NotNull
#NotEmpty
private final CharSequence pass;
#JsonCreator
public PasswordCredential(
#Nullable #JsonProperty( value = "user", access = JsonProperty.Access.WRITE_ONLY ) String user,
#Nullable #JsonProperty( value = "pass", access = JsonProperty.Access.WRITE_ONLY ) CharSequence pass
) {
this.user = user;
this.pass = pass;
}
I would like to convert it to a JsonSchema so that I can return it as SDR would. How can I accomplish this?
I'm not familiar with Spring, but we convert DTOs to string using Gson. This is just a test, but you get the idea.
import com.google.gson.GsonBuilder;
public class NewMain {
static public class PasswordCredential {
private String user;
private CharSequence pass;
}
public static void main(String[] args) {
PasswordCredential pc = new PasswordCredential();
pc.pass = "password";
pc.user = "myuser";
GsonBuilder builder = new GsonBuilder();
System.out.println(builder.create().toJson(pc));
}
}
If that's not what you're looking let me know, so I can expand on my answer.

Jackson XML tag and attribute with the same name

I want to get the following XML:
<User id="two">
<id>one</id>
</User>
And I try to use Jackson XML mapper for this:
#JacksonXmlRootElement
public class User {
private String id;
private String attributeId;
public User(final String id, final String attributeId) {
this.id = id;
this.attributeId = attributeId;
}
#JacksonXmlProperty(localName = "id")
public String getId() {
return id;
}
#JacksonXmlProperty(localName = "id", isAttribute = true)
public String getAttributeId() {
return attributeId;
}
public static void main(String[] args) throws IOException {
final XmlMapper xmlMapper = new XmlMapper();
final File file = new File("user.xml");
final User user = new User("one", "two");
xmlMapper.writeValue(file, user);
}
}
But all I get is this exception
java.lang.IllegalArgumentException: Conflicting getter definitions for property "id": com.sbconverter.parser.slovoed.User#getId(0 params) vs com.sbconverter.parser.slovoed.User#getAttributeId(0 params)
Can I have same name of the attribute and tag, on one object?
This is a known problem, so you'll need to do extra classes for this case.
Adding a space in front of id at localName (localName = " id") can do the trick, but it's more recommended to make a new bean.

Jackson JSON field mapping capitalization?

I'm not clear how jackson deals with capitalization in mapping fields. If anyone could help I'd appreciate it.
{"user":{"username":"user#host.com","password":"pwd","sendercompid":"COMPID","service":{"host":"address","port":6666,"service":"S1","serviceAsString":"s1"}},"MDReqID":"ghost30022","NoRelatedSym":1,"Symbol":["GOOG"],"MarketDepth":"0","NoMDEntryTypes":3,"MDEntryType":["0","1","2"],"SubscriptionRequestType":"1","AggregatedBook":"N"}:
Above is my json, below is my exception...
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "MDReqID" (class com.myco.qa.fixrest.MarketDataRequest), not marked as ignorable (10 known properties: , "mdreqID", "marketDepth", "user", "subscriptionRequestType", "aggregatedBook", "mdentryType", "symbol", "mdupdateType", "noRelatedSym", "noMDEntryTypes"])
Above is my exception, below is my class...
public class MarketDataRequest {
private User user;
private String MDReqID;
private char SubscriptionRequestType;
private int MarketDepth;
private int MDUpdateType;
private char AggregatedBook;
private int NoMDEntryTypes;
private ArrayList<Character> MDEntryType;
private int NoRelatedSym;
private ArrayList<String> Symbol;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public String getMDReqID() {
return MDReqID;
}
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
public char getSubscriptionRequestType() {
return SubscriptionRequestType;
}
public void setSubscriptionRequestType(char subscriptionRequestType) {
SubscriptionRequestType = subscriptionRequestType;
}
... et cetera
Since your setter method is named setMDReqID(…) Jackson assumes the variable is named mDReqID because of the Java naming conventions (variables should start with lower case letters).
If you really want a capital letter use the #JsonProperty annotation on the setter (or - for serialization - on the getter) like this:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
You can also do
#JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class)
on the class to capitalise all property names in the JSON message
Add #JsonProperty on the setter that matches the property name in your received JSON string:
#JsonProperty("MDReqID")
public void setMDReqID(String MDReqID) {
this.MDReqID = MDReqID;
}
Additionally add #JsonProperty annotation to the getter as well for your output to appear in the conventional format:
#JsonProperty("mDReqID")
public String getMDReqID() {
return MDReqID;
}
Now you can name your variable whatever you like:
private String mdReqID;
I solve this problem by:
#Getter
#Setter
static class UserInfo {
//#JsonProperty("UUID")
private String UUID = "11";
private String UserName = "22";
private String userName = "33";
private String user_Name = "44";
private String user_name = "55";
private String User_name = "66";
private boolean HasDeleted=true;
private boolean hasDeleted=true;
private boolean has_Deleted=true;
private boolean has_deleted=true;
private boolean HAS_DELETED=true;
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
String s = objectMapper.writeValueAsString(new UserInfo());
System.out.println(s);
UserInfo userInfo = objectMapper.readValue(s, UserInfo.class);
System.out.println(objectMapper.writeValueAsString(userInfo));
}
output:
{"UUID":"11","UserName":"22","userName":"33","user_Name":"44","user_name":"55","User_name":"66","HasDeleted":true,"hasDeleted":true,"has_Deleted":true,"has_deleted":true,"HAS_DELETED":true}
I face the same problem , after have try UpperCamelCaseStrategy but still this error occurred , the strategy made my field pContent to ObjectMapper property Pcontent, as not want to add #JsonProperty for every field, simply use gson instead at last
Use JsonNaming Annotation to get all Class Field Names in Proper Case
Use lombok.Data Annotation to automatically make it work without adding getters and setters in your class
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import lombok.Data;
#JsonNaming(PropertyNamingStrategies.UpperCamelCaseStrategy.class)
#Data

Struts2 + Json Serialization of items

I have the following classes:
public class Student {
private Long id ;
private String firstName;
private String lastName;
private Set<Enrollment> enroll = new HashSet<Enrollment>();
//Setters and getters
}
public class Enrollment {
private Student student;
private Course course;
Long enrollId;
//Setters and Getters
}
I have Struts2 controller and I would like to to return Serialized instance of Class Student only.
#ParentPackage("json-default")
public class JsonAction extends ActionSupport{
private Student student;
#Autowired
DbService dbService;
public String populate(){
return "populate";
}
#Action(value="/getJson", results = {
#Result(name="success", type="json")})
public String test(){
student = dbService.getSudent(new Long(1));
return "success";
}
#JSON(name="student")
public Student getStudent() {
return student;
}
public void setStudent(Student student) {
this.student = student;
}
}
It returns me the serializable student object with all sub classes, but I would like to have only student object without the hashset returned .
How can I tell Struts to serialize only the object?
I do have Lazy loading enabled and hashset is returned as proxy class.
See the answer here which shows the use of include and exclude properties. I don't think the example clearly shows excluding nested objects however I have used it for this purpose. If you still have issues I'll post a regex which will demonstrate this.
Problem with Json plugin in Struts 2
Edit:
Here is an example of using exclude properties in an annotation which blocks the serialization of a nested member:
#ParentPackage("json-default")
#Result(type = "json", params = {
"excludeProperties",
"^inventoryHistory\\[\\d+\\]\\.intrnmst, selectedTransactionNames, transactionNames"
})
public class InventoryHistoryAction extends ActionSupport {
...
inventoryHistory is of type InventoryHistory a JPA entity object, intrnmst references another table but because of lazy loading if it were serialized it would cause an Exception when the action is JSON serialized for this reason the exclude parameter has been added to prevent this.
Note that
\\
is required for each \ character, so a single \ would only be used in the xml where two are required because of escaping for the string to be parsed right.
#Controller
#Results({
#Result(name="json",type="json"
, params={"root","outDataMap","excludeNullProperties","true"
,"excludeProperties","^ret\\[\\d+\\]\\.city\\.province,^ret\\[\\d+\\]\\.enterprise\\.userinfos","enableGZIP","true"
})
})
public class UserinfoAction extends BaseAction {
#Action(value="login")
public String login(){
if(jsonQueryParam!=null && jsonQueryParam.length()>0)
{
user = JsonMapper.fromJson(jsonQueryParam, TUserinfo.class);
}
Assert.notNull(user);
//RESULT="ret" addOutJsonData: put List<TUserinfo> into outDataMap with key RESULT for struts2 JSONResult
addOutJsonData(RESULT, service.login(user));
return JSON;
}
public class TUserinfo implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String userid;
private String username;
private String userpwd;
private TEnterpriseinfo enterprise;
private String telphone;
private TCity city;
......
}
public class TEnterpriseinfo implements java.io.Serializable {
private String enterpriseid;
private String enterprisename;
private Set<TUserinfo> userinfos = new HashSet<TUserinfo>(0);
.......}
before set the excludeProperties property,the result is below:
{"ret":[
{
"city":{"cityename":"tianjin","cityid":"12","cityname":"天津"
,"province": {"provinceename":"tianjing","provinceid":"02","provincename":"天津"}
}
,"createddate":"2014-01-07T11:13:58"
,"enterprise":{"createddate":"2014-01-07T08:38:00","enterpriseid":"402880a5436a227501436a2277140000","enterprisename":"测试企业2","enterprisestate":0
,"userinfos":[null,{"city":{"cityename":"beijing","cityid":"11","cityname":"北京","province":{"provinceename":"beijing","provinceid":"01","provincename":"北京市"}
},"comments":"ceshi","createddate":"2004-05-07T21:23:44","enterprise":null,"lastlogindate":"2014-01-08T08:50:34","logincount":11,"telphone":"2","userid":"402880a5436a215101436a2156e10000","username":"0.5833032879881197","userpwd":"12","userstate":1,"usertype":0}]
}
,"lastlogindate":"2014-01-08T10:32:43","logincount":0,"telphone":"2","userid":"402880a5436ab13701436ab1b74a0000","username":"testUser","userpwd":"333","userstate":1,"usertype":0}]
}
after set the excludeProperties property,there are not exist province and userinfos nodes, the result is below:
{"ret":
[{
"city":{"cityename":"tianjin","cityid":"12","cityname":"天津"}
,"createddate":"2014-01-07T11:13:58"
,"enterprise":{"createddate":"2014-01-07T08:38:00","enterpriseid":"402880a5436a227501436a2277140000","enterprisename":"测试企业2","enterprisestate":0}
,"lastlogindate":"2014-01-08T11:05:32","logincount":0,"telphone":"2","userid":"402880a5436ab13701436ab1b74a0000","username":"testUser","userpwd":"333","userstate":1,"usertype":0
}]
}

Categories

Resources