Let's say I have a JSON object hierarchy like the following:
{
"name": "Mosquito Laser",
"configurations": [{
"currency": "USD",
"price": "10.00" /* the Basic option */
}, {
"currency": "USD",
"price": "50.00" /* the Pro option */
}, ]
}
I would like to deserialize this json into a java object, and flatten it into a single level. So for example, I would like to map the above json into the following java class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product {
#JsonProperty
protected String name;
protected String lowestPrice;
protected String highestPrice;
}
I would like to use a custom method to compute the lowestPrice and highestPrice fields from the list of configurations in the json. Assume for the sake of argument that the json hierarchy and the java object have been simplified here for clarity, and that in reality they are actually much more complicated so I do not wish to implement a completely custom deserializer. I want most of the fields to be automatically deserialized using Jackson's databinding defaults, but I want to specify custom operations for certain fields.
Is there an easy way to tell Jackson to use a special method to compute the value of the lowestPrice and highestPrice fields automatically?
Use:
#JsonProperty("configuration")
#JsonDeserialize(using = ConfigurationDeserializer.class)
protected String cheapestPrice;
And a deserializer looks like this:
public class ConfigurationDeserializer extends JsonDeserializer<String> {
#Override
public String deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException {
(your logic to go from the configuration JSON to cheapestPrice goes here)
}
}
In ETL and SQL, this is aggregation. A couple of questions:
Do you need the values being aggregated?
Do you need other values from lower level JSON?
Is memory a concern?
Is CPU a concern?
Comments:
Aggregation requires saving state when parsing tree-like structures
Fast streaming parsers don't save state
State can either be passed into children or can be returned back from children.
Jackson can be slow when parsing unneeded child values.
If all you need is to modify values just define getter method(s):
public class Product {
public String name;
protected String lowestPrice;
protected String highestPrice;
public int getLowestPrices() {
return calculateLowest(lowestPrice);
}
// and similarly for highestPrice...
}
or, when reading JSON in, define matching setter. Methods have precedence over fields.
Related
I have a variety of JSON files (with slightly different schemas) flowing on Kinesis. Their Schema is really complex. They are user raw hits and their schema is super complex. I would like to create a Single POJO to represent all underneath messages (Something Spark does internally by creating a Single schema). I was trying to use GSON library but the only way to accomplish this is, by writing a custom deserializer and in that case, I will end up writing deserialization logic for all the fields in those JSONs.
Is there any way where we can only overwrite the deserialization of a few fields only and the rest of the fields can still be deserialized by GSON as a default way?
JSON-1
{
"first_col":"abc",
"second_col":false
}
JSON-2
{
"first_col":"abc",
"second_col":"false-String"
}
JSON-3
{
"first_col":"abc",
"second_col":{
"col1":"xyz",
"col2":123
}
}
Common POJO
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class Example {
private String firstCol;
private String secondCol;
public String getFirstCol() {
return firstCol;
}
public void setFirstCol(String firstCol) {
this.firstCol = firstCol;
}
public Boolean getSecondCol() {
return secondCol;
}
public void setSecondCol(String secondCol) {
this.secondCol = secondCol;
}
}
So basically second_col could be boolean, string, or complex object. first_col is always a string. So I don't want to write deserialize logic for first_col. I want to write deserialize logic for only second_col and deserialize it to string only and the downstream consumer will take care of converting it to the right type before consuming this field.
I am attempting to implement the HL7 FHIR spec's assertion that JSON representing a FHIR model will not have empty objects nor empty arrays. For the sake of not making the lives of my consumers any harder, I'm not strictly enforcing this during deserialization, but I want to ensure the serialized JSON produced by my library conforms as specified. I am using Java and Jackson ObjectMapper to serialize Objects into JSON. My understanding from writing a custom serializer is that the Object is at one point represented as JsonNode, regardless of what you are converting to.
What I would like to do is intercept the JsonNode as it exits the serializer, make some adjustments to it (find and remove empty arrays and objects), and then let it continue on its way. I need to do this in an environment where I can't tweak the ObjectMapper, because I don't have access to it. And further, the complex hierarchy of models in this library use Jackson's default serialization with annotations etc. heavily, and I cannot eliminate this.
If I go the route of defining a custom serializer for the base type, let's say "Resource", then I have a problem, because I still need the original serializer's output in order to generate my modified output. And further, that needs to accommodate any custom serializers that may already exist on various types within the model.
I got pretty far with the above option using https://www.baeldung.com/jackson-call-default-serializer-from-custom-serializer and the last option, implementing BeanSerializerModifier, but I ran into the issue where I can't control the ObjectMapper that my library consumers use.
Example POJOs (Using Lombok for accessors):
#Data
#JsonInclude(JsonInclude.Include.NON_EMPTY)
#JsonIgnoreProperties(ignoreUnknown = true)
abstract class Resource {
private FhirString id;
private List<Extension> extension;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
public abstract ResourceType getResourceType();
}
#Data
#Builder
class SomethingElse extends Resource {
FhirUri someProperty;
CodeableConcept someCode;
List<Reference> someReferences;
#Override
public ResourceType getResourceType() {
return ResourceType.SOMETHING_ELSE;
}
}
And an example instance of the SomethingElse class:
SomethingElse somethingElse = SomethingElse.builder()
.someProperty(FhirUri.from("some-simple-uri"))
.someCode(new CodeableConcept())
.someReference(List.of(new Reference()))
.build();
somethingElse.setId(FhirString.randomUuid());
somethingElse.setExtension(new ArrayList<>());
When I tell any mapper (or, for example, use a Spring service) to map the SomethingElse class into JsonNode, I can, for example, end up with empty objects and arrays, like this:
ObjectMapper mapper = getUntouchableMapper();
JsonNode somethingElseNode = mapper.valueToTree(somethingElse);
System.out.println(somethingElseNode.toString());
Becomes:
{
"resourceType": "SomethingElse",
"id": "00000000-0002-0004-0000-000000000000",
"someProperty": "some-simple-uri",
"someCode": {},
"someReferences": [{}],
"extension": []
}
According to FHIR, this should actually look like:
{
"resourceType": "SomethingElse",
"id": "00000000-0002-0004-0000-000000000000",
"someProperty": "some-simple-uri"
}
To summarize
How do I preserve the serialization mechanisms already in place, regardless of the ObjectMapper used, and somehow remove empty lists and objects from outgoing JSON produced by the Jackson serialization process?
Edit:
I also tried #JsonInclude(JsonInclude.Include.NON_EMPTY), which did omit empty list implementations. However, the vast majority of data in this library is represented by POJOs that serialize to maps and primitives, and this annotation only works if they are represented directly by maps and primitives in the model.
The solution is to use a custom #JsonInclude, which is new in Jackson 2.9. Thank you #dai for pointing me back towards this functionality.
On the base Resource class, this looks like:
#JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = FhirJsonValueFilter.class)
class Resource implements FhirTypeInterface {
...
#Override
public boolean isEmpty() {
//Details omitted for simplicity
}
}
For visibility, the interface used above:
interface FhirTypeInterface {
boolean isEmpty();
}
And my custom definition for FhirJsonValueFilter implements all of the functionality of JsonInclude.Include.NON_EMPTY but also adds functionality for checking against a method implemented by FHIR types (implementation of this is not relevant to the answer).
public class FhirJsonValueFilter {
#Override
public boolean equals(Object value) {
return !getWillInclude(value);
}
/**
* Returns true for an object that matched filter criteria (will be
* included) and false for those to omit from the response.
*/
public boolean getWillInclude(Object value) {
//Omit explicit null values
if (null == value) {
return false;
}
//Omit empty collections
if (Collection.class.isAssignableFrom(value.getClass())) {
return !((Collection) value).isEmpty();
}
//Omit empty maps
if (Map.class.isAssignableFrom(value.getClass())) {
return !((Map) value).isEmpty();
}
//Omit empty char sequences (Strings, etc.)
if (CharSequence.class.isAssignableFrom(value.getClass())) {
return ((CharSequence) value).length() > 0;
}
//Omit empty FHIR data represented by an object
if (FhirTypeInterface.class.isAssignableFrom(value.getClass())) {
return !((FhirTypeInterface) value).isEmpty();
}
//If we missed something, default to include it
return true;
}
}
Note that the custom omission filter uses Java's Object.equals functionality, where true means to omit the property, and I've used a second method to reduce confusion in this answer.
My REST service needs to return messages of format:
{
"code": 1000,
"message": "Success",
"description": null,
"data": [
{
"custId": "00000023401443",
"engName": "MISTER NORWAYTEST'S PASSPORT",
}
]
}
where the first tier of the JSON message is basically like a message header to provide technical details about message delivery while the "data" key contains the actual business information.
If I were to create a class to reflect this, I would by default come out with something that looks like this:
public class ResponseModel<T> implements Serializable{
private Integer code;
private String message;
private String description;
#JsonProperty(value = "data")
private T dataObj;
}
but doing it this way causes my controllers to all return the same object with no real business context value to the class name and reduces readability:
#GetMapping("/profile/{userId}")
public ResponseEntity<ResponseModel> getProfile(#PathVariable String userId) {
...
}
What I would like to do is to use ResponseModel as a superclass and then inherit them into subclasses with real business context names (e.g. Customer or Account). But in order to adhere to the required JSON format, I need to ensure that attributes of the subclass as wrapped into the "data" key.
Is there a way that I can do that? Using #JsonRootName would also wrap the superclass properties as well.
The problem of your the format you want is right here :
"data": *[* <---- HERE THAT BRACKET
If you want a bracket that means your data is a list so you need to fix it by making dataObj a list :
#JsonProperty(value = "data")
private List<T> dataObj;
Now i don't see the point of subclassing ResponseModel you could just do the following :
public ResponseEntity<ResponseModel> myControllerMethod(){
List<Account> list = myService.readAccounts();
return new ResponseModel<Account>(list);//default code OK,...
}
If you make your Business classes inherits ResponseModel which is only a wrapper to handle controller's result, you will be mixing your business layer with controller's specific layer which is not a good idea.
but doing it this way causes my controllers to all return the same object with no real business context value to the class name and reduces readability:
In a Java's controller, you're suppose to have very very few lines, basically, call a service, check the answer/handle exceptions, return the response. So I don't see any problem of readability because if this. If you talk about the generic returned value, just name properly your methods.
I'm using Jackson to deserialize JSON from a ReST API to Java objects using Jackson.
The issue I've run into is that one particular ReST response comes back wrapped in an object referenced by a numeric identifier, like this:
{
"1443": [
/* these are the objects I actually care about */
{
"name": "V1",
"count": 1999,
"distinctCount": 1999
/* other properties */
},
{
"name": "V2",
"count": 1999,
"distinctCount": 42
/* other properties */
},
...
]
}
My (perhaps naive) approach to deserializing JSON up until this point has been to create mirror-image POJOs and letting Jackson map all of the fields simply and automatically, which it does nicely.
The problem is that the ReST response JSON has a dynamic, numeric reference to the array of POJOs that I actually need. I can't create a mirror-image wrapper POJO because the property name itself is both dynamic and an illegal Java property name.
I'd appreciate any and all suggestions for routes I can investigate.
The easiest solution without custom deserializers is to use #JsonAnySetter. Jackson will call the method with this annotation for every unmapped property.
For example:
public class Wrapper {
public List<Stuff> stuff;
// this will get called for every key in the root object
#JsonAnySetter
public void set(String code, List<Stuff> stuff) {
// code is "1443", stuff is the list with stuff
this.stuff = stuff;
}
}
// simple stuff class with everything public for demonstration only
public class Stuff {
public String name;
public int count;
public int distinctCount;
}
To use it you can just do:
new ObjectMapper().readValue(myJson, Wrapper.class);
For the other way around you can use #JsonAnyGetter which should return a Map<String, List<Stuff>) in this case.
I think the easiest solution is to use a custom JsonDeserializer. It allows you to parse the input step by step and to extract only those information you need to build your object.
Here is a simple example how to implement a custom deserializer: custom jackson deserializer
Let's say I have Java classes that looks like this:
public class A {
public String name;
public B b;
}
public class B {
public int foo;
public String bar;
}
I want to serialize an instance of A into JSON. I am going to use the ObjectMapper class from Jackson:
A a = new A(...);
String json = new ObjectMapper().writeValueAsString(a);
Using this code, my JSON would look like this:
{
"name": "MyExample",
"b": {
"foo": 1,
"bar": "something"
}
}
Instead, I want to annotate my Java classes so that the generated JSON will instead look like this:
{
"name", "MyExample",
"foo": 1,
"bar": "something"
}
Any ideas?
Personally I think you may be better off mapping structure to structure, and not doing additional transformations.
But if you do want to go with the plan, just use Jackson 2.x, and add #JsonUnwrapped annotation on property b. That should do the trick.