im using fasterxml jackson to serialize an entity in a java project, the problem im having is the following one, when the entity is converted to JSON I get something like this:
{ "#id":1
"father":{
"#id":2,
"name":"robert smith"
"country":{"#id":3, "countryId":10, "countryName":"USA"}
},
"mother":{
"#id":4,
"name":"mary dune"
"country":{#ref:3}
}
}
Father and Mother has a reference to the same Country object, but when is serialized mother only have the reference to #id:3. I know that this is done to avoid circular refernce and that kind of stuff, but in this case I need that with the country entity it doesnt use reference and just put the whole entity.
Is there some kind of annotation to achieve that? like putting an annotation in the country entity? I just want this with country and no other entity, I want the other ones to respect the ref system.
The result im looking for is something like:
{"#id":1
"father":{
"#id":2,
"name":"robert smith"
"country":{"#id":3, "countryId":10, "countryName":"USA"}
},
"mother":{
"#id":4,
"name":"mary dune"
"country":{"#id":3, "countryId":10, "countryName":"USA"}
}
}
This is the objectMapper im using:
public ObjectMapperContextResolver() {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(
LocalDateTime.class,
new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
javaTimeModule.addSerializer(
LocalDate.class,
new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
javaTimeModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
javaTimeModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
defaultMapper.registerModule(new Jdk8Module())
.registerModule(javaTimeModule)
.registerModule(new BlankStringsAsNullModule())
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true)
.configure(SerializationFeature.USE_EQUALITY_FOR_OBJECT_ID, true);
}
Related
All the answers on stackoverflow regarding Jackson I found deal with only single root node unwrapping for JSONs like
{
"user":
{
"name":"Sam Smith",
"age":1
}
}
The solution is to either use wrapper classes or use .withRootName("user") call like this
User user = objectMapper.reader()
.forType(User.class)
.withRootName("user")
.readValue(string);
also annotating User class with#JsonRootName(value = "user") is the option.
But is there an option to NOT use wrapper classes for JSONs with several parallel root nodes like this:
{
"user":
{
"name":"Sam Smith",
"age":1
},
"timestamp":
{
"clickpoint":"AE12",
"purchasable":"false"
}
}
. Can't find a solution for that. Jackson will throw an exception of not matching root name "timestamp" with expected "user". Thank you for help if you know the answer.
To operate with objects without named root you can deal with JsonNode like in the example below:
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.reader().readTree(source);
User user = mapper.treeToValue(node.get("user"), User.class);
Timestamp timestamp = mapper.treeToValue(node.get("timestamp"), Timestamp.class);
System.out.println(user.getName());
System.out.println(timestamp.getClickpoint());
In older version of Jackson instead of treeToValue() you can use readValue() with the same arguments.
I have a Spring Boot app which is using MySQL db.
At the moment I'm trying to do the following:
- deserialize instances from the *.csv files;
- inject them into the MySQL db.
For the simple instances there are no issues. But in case if I have an object with ManyToMany or OneToMany relations, deserialization is not working correctly. Currently I'm using Jackson dependency for *.csv deserialization:
CsvMapper csvMapper = new CsvMapper();
csvMapper.disable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
CsvSchema csvSchema = csvMapper.schemaFor(type).withHeader().withColumnSeparator(';').withLineSeparator("\n");
MappingIterator<Object> mappingIterator = csvMapper.readerFor(type).with(csvSchema).readValues(csv);
List<Object> objects = new ArrayList<>();
while (mappingIterator.hasNext()) {
objects.add(mappingIterator.next());
}
Example of the instance with many to many: (Idea is that one app can have different versions)
public class Application {
private Long id;
private String name;
#OneToMany(mappedBy = "application")
private Set<Version> versions = new HashSet<>();
}
For insertion into the DB I'm using Spring Boot entities that are #Autowired.
My first question is - what should I put as an input into the CSV file to deserialize it correctly ? Because if I have :
id;name;
1;testName;
(skipping versions), I'm having a trouble. The same even if I try to put some values into the version. So I don't know how to provide correctly the input for Jackson CSV deserialization in case of SET + later, how can I persist this entity ? Should I first put all the versions into the DB and then try to put applications?
Any thoughts? Thanks in advance!
Use ApacheCommons to parse csv.
final byte[] sourceCsv;
String csvString = new String(sourceCsv);
CSVFormat csvFormat = CSVFormat.DEFAULT;
List<CSVRecord> csvRecord = csvFormat.parse(new
StringReader(csvString)).getRecords();
It will help you to deserialize and to store in database.
I'm working with the MarkLogic POJO Databinding Interface at the moment. I'm able to write POJOs to MarkLogic. Now I want to search those POJOs and retrieve the search results. I'm following the instructions from: https://docs.marklogic.com/guide/java/binding#id_89573 However, the search results don't seem to return the correct objects. I'm getting a JSONMappingException. Here's the code:
HashMap<String, MatchedPropertyInfo> matchedProperties = new HashMap<String, MatchedPropertyInfo>();
PropertyMatches PM = new PropertyMatches(123,"uri/prefix/location2", "uri/prefix", 1234,0,"/aKey","/aLocation",true,matchedProperties);
MatchedPropertyInfo MPI1 = new MatchedPropertyInfo("matched/property/uri1", "matched/property/key1", "matched/property/location1", true,"ValueMatch1", 12, 1*1.0/3, true);
MatchedPropertyInfo MPI2 = new MatchedPropertyInfo("matched/property/uri2", "matched/property/key2", "matched/property/location2", true,"ValueMatch2", 14, 1.0/2.0, true);
PM.getMatchedProperties().put("matched/property/prefix/location1", MPI1);
PM.getMatchedProperties().put("matched/property/prefix/location2", MPI2);
PojoRepository myClassRepo = client.newPojoRepository(PropertyMatches.class, Long.class);
myClassRepo.write(PM);
PojoQueryBuilder qb = myClassRepo.getQueryBuilder();
PojoPage<PropertyMatches> matches = myClassRepo.search(qb.value("uri", "uri/prefix/location2"),1);
if (matches.hasContent()) {
while (matches.hasNext()) {
PropertyMatches aPM = matches.next();
System.out.println(" " + aPM.getURI());
}
} else {
System.out.println(" No matches");
}
The PropertyMatches (PM) object is succesfully written to the MarkLogic database. This class contains a member: private String URI which is initiated with "uri/prefix/location2". The matches.hasContent() returns true in the example above. However, I'm getting an error on PropertyMatches aPM = matches.next();
Searching POJOs in MarkLogic and read them into your Java program requires the POJOs to have an empty constructor. In this case PropertyMatches should have public PropertyMatches(){} and MatchedPropertyInfo should have public MatchedPropertyInfo(){}
Thanks #sjoerd999 for posting the answer you found. Just to add some documentation references, this topic is discussed here: http://docs.marklogic.com/guide/java/binding#id_54408 and here: https://docs.marklogic.com/javadoc/client/com/marklogic/client/pojo/PojoRepository.html.
Also worth noting is you can have multiple parameters in the consructor, you just have to do it the Jackson way. Here are examples of two ways (with annotations and without): https://manosnikolaidis.wordpress.com/2015/08/25/jackson-without-annotations/
I'd recommend using annotations as that's built-in with Jackson. But if you want to do it without annotations, here's the code:
ObjectMapper mapper = new ObjectMapper();
// Avoid having to annotate the Person class
// Requires Java 8, pass -parameters to javac
// and jackson-module-parameter-names as a dependency
mapper.registerModule(new ParameterNamesModule());
// make private fields of Person visible to Jackson
mapper.setVisibility(FIELD, ANY);
If you want to do this with PojoRepository you'll have to use the unsupported getObjectMapper method to get the ObjectMapper and call registerModule and setVisibility on that:
ObjectMapper objectMapper = ((PojoRepositoryImpl) myClassRepo).getObjectMapper();
OK, I am adding a couple of custom claims to the payload when I generate the JWT, and I can pull those out just fine in my front-end (javascript). I then have my javascript send an ajax call to a micro-service and it passes the JWT along with it. I want to get my custom claims out of the JWT in the micro-service. I'm doing the following:
Claims claims = Jwts.parser().setSigningKey(Vars.SECRET_KEY).parseClaimsJws(token).getBody();
User user = claims.get("customuser", User.class);
and it throws an exception.
io.jsonwebtoken.RequiredTypeException: Expected value to be of type: class net.netdatacorp.netdauth.model.User, but was class java.util.LinkedHashMap
at io.jsonwebtoken.impl.DefaultClaims.get(DefaultClaims.java:128)
Here is how the data looks in the JWT inspector on the front-end for my custom claim.
{
jti: "83bffbad-7d36-4370-9332-21a84f2a3dce",
iat: 1498241526,
sub: "test",
iss: "www.test.net",
customuser: {
userId: 1,
userCd: "TMM",
firstNm: "Testy",
lastNm: "McTesty",
userNm: "test",
emailAddress: "jacob#test.net",
active: true,
createdDt: 1491355712000,
createdByUserId: 0,
lastUpdateDt: 1498199278000,
lastUpdateByUserId: 0,
lastLoginDt: 1484928016000
}
}
What am I missing to be able to pull my custom claim out?
We can use Jackson's object mapper to convert Claims (which is a Map<String, Object>) to our custom claim java object.
final ObjectMapper mapper = new ObjectMapper();
Claims jwsMap = Jwts.parser()
.setSigningKey("SECRET")
.parseClaimsJws("jwt")
.getBody();
return mapper.convertValue(jwsMap, MyCustomClaim.class);
Also add that code to try catch to make sure we handle the case of missing/tampered signature.
Add Custom Claims to JWT.
Note: I used this in Spring Security
Reserved Claims
iss – Issuer
sub – Subject
aud – Audience
exp – Expiration
nbf – Not Before
iat – Issued At
jti – JWT ID
Adding Custom Claims
String token = Jwts.builder()
.setSubject(subject)
.setExpiration(expDate)
.claim("userId", "3232")
.claim("UserRole", "Admin")
.signWith(SignatureAlgorithm.HS512, secret )
.compact();
Retrieving Custom Claims
Claims claims = Jwts.parser()
.setSigningKey(tokenSecret)
.parseClaimsJws(jwt).getBody();
// Reading Reserved Claims
System.out.println("Subject: " + claims.getSubject());
System.out.println("Expiration: " + claims.getExpiration());
// Reading Custom Claims
System.out.println("userId: " + claims.get("userId"));
System.out.println("userRole: " + claims.get("userRole"));
JJWT has had this functionality since its 0.11.0 release.
The idea is that a JWT library should not itself undertake marshaling behavior because 1) it's really complex work to be able to handle any ad-hoc data structure (see the JAXB and Jackson codebases as examples) and 2) the JSON marshaller can do it already - there's no point in JJWT re-inventing that wheel.
So, to leverage the marshaller's built-in support, we need to tell it what fields it should look to unmarshall into custom objects so it can do this at parse time. (By the time the JSON is fully parsed, it's already 'too late' when JJWT starts looking at the JWT Map, so we need to ensure the marshaller can do it at parse time).
You do this by telling the marshaller which fields should be converted into custom types, for example, with Jackson:
Jwts.parserBuilder()
.deserializeJsonWith(new JacksonDeserializer(Maps.of("user", User.class).build())) // <-----
.build()
.parseClaimsJwt(aJwtString)
.getBody()
.get("user", User.class) // <-----
For more information, see JJWT's documentation at https://github.com/jwtk/jjwt#parsing-of-custom-claim-types
OK, so I switched to using Jose4J instead of JJWT and after working to get every thing working I realized I probably could have done something similar with JJWT. So what I ended up doing was to use Gson to perform a JSON encoding of the Object and the attaching the resulting JSON string as a claim. And so when I wanted to get a custom claim back out, I would extract the claim as a string and the use the Gson library to convert it back to a POJO.
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.create();
JwtConsumer jwtConsumer = getConsumer();
JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
String userStr = jwtClaims.getClaimValue("user", String.class);
User user = gson.fromJson(userStr, User.class);
I know that your main target is Customer object. other data already exist in the object of the claim. you can easily manage your own object like this.
#Data //insted of this annotation, you can generate the getters and setters
#JsonIgnoreProperties(ignoreUnknown = true)
public class Customer {
private Integer userId;
private String userCd;
private String firstNm;
........
}
JsonIgnoreProperties annotation is very important when converts to the object from the token body. it ignores the other Properties the object hasn't. (Jti,Lat,Iss)
now you have the object that you want. let's generate the token.
Map<String, Object> claims = new HashMap<>(); //create a hashmap
Customer customer= new Customer(); //create your object
//assign the initial customer data
customer.setUserId(1);
customer.setUserCd("TMM");
customer.setFirstNm("Testy");
ObjectMapper oMapper = new ObjectMapper(); //create a objectmapper object
Map<String, Object> customerData = oMapper.convertValue(customer, Map.class); //convert the customer object into map of (String, Object)
claims.putAll(customerData ); //put all the customer data into claims map
//create the token using another required data
String token = Jwts.builder()
.setClaims(claims) //this our object
.setSubject("test")
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
.signWith(SignatureAlgorithm.HS512, "secret")
.compact();
go to the https://jwt.io/ and put the generated token and see how is it. it will be like this.
{
"sub": "test",
"firstNm": "Testy", //customer data from the object
"exp": 1622862855,
"userId": 1, //customer data from the object
"iat": 1622844855,
"userCd": "TMM" //customer data from the object,
........
}
it contains all the data with your custom customer data too.
now let's decode the token
Jws<Claims> claimsJws = Jwts.parser().setSigningKey("secret").parseClaimsJws(token);
ObjectMapper mapper = new ObjectMapper();
Customer customer = mapper.convertValue(claimsJws.getBody(), Customer.class); //convert the claims body by mentioning the customer object class
System.out.println("customerData = " + customer);
now you can use the customer data object as you want.
**special thing is the #JsonIgnoreProperties(ignoreUnknown = true) annotation.
I want to create JSON Schema manually using GSON but i dont find any JsonSchema element support in GSON. I dont want to convert a pojo to schema but want to create schema programatically . Is there any way in GSON ? May be something like following.
**1 JsonSchema schema = new JsonSchema();
2 schema.Type = JsonSchemaType.Object;
3 schema.Properties = new Dictionary<string, JsonSchema>
4{
5 { "name", new JsonSchema { Type = JsonSchemaType.String } },
6 {
7 "hobbies", new JsonSchema
8 {
9 Type = JsonSchemaType.Array,
10 Items = new List<JsonSchema> { new JsonSchema { Type = JsonSchemaType.String } }
11 }
12 },
13};**
You may consider using everit-org/json-schema for programmatically creating JSON Schemas. Although it is not properly documented, its builder classes form a fluent API which lets you do it. Example:
Schema schema = ObjectSchema.builder()
.addPropertySchema("name", StringSchema.builder().build())
.addPropertySchema("hobbies", ArraySchema.builder()
.allItemSchema(StringSchema.builder().build())
.build())
.build();
It is slightly different syntax than what you described, but it can be good for the same purpose.
(disclaimer: I'm the author of everit-org/json-schema)
I tried to build a schema as suggested above, see Everit schema builder includes unset properties as null