I have a class with some field
public class SomeClass {
private Duration discussionTime;
}
When i try to send this class to frontend using #RestController in Spring, i see that answer:
"discussionTime": {
"seconds": 7,
"zero": false,
"negative": false,
"nano": 72000000,
"units": [
"SECONDS",
"NANOS"
]
}
Is there ways to set format of answer to
"discussionTime": 7 ?
There are two ways you can do this:
custom mapping to a new object (instead of returning a Duration)
by creating your own serializer: serialize/deserialize java 8 java.time with Jackson JSON mapper
But if you only want the seconds, might be best to just create a new object you serialize instead of messing with the Duration.
An option could be to use a MixIn.
Also JavaTimeModule could help you.
Depending on your use case you can create your own serializer as suggested by others or go for a more generic solution if you have to do this in multiple places.
Or maybe:
public class SomeClass {
private Duration discussionTime;
#JsonProperty("discussionTime")
public long getDiscussionTimeSeconds() {
return discussionTime.getSeconds();
}
}
Related
I'm trying to write some code that will deserialize JSON into somebody elses class. That is, I don't own the target class so I can't annotate it.
In particular, this class has some helper methods that are complicating the deserialization process. Something like this:
class Result {
private List<String> ids;
public List<String> getIds() {
return ids;
}
public void setIds(List<String> ids) {
this.ids = ids;
}
// Helpers
public String getId() {
return this.ids.isEmpty() ? null : this.ids.get(0);
}
public void setId(String id) {
this.ids = List.of(id);
}
}
When serialized, we get both ids and id come through as fields:
{
"ids": ["1", "2", "3"],
"id": "1"
}
And then when deserializing this JSON, Jackson is calling both setters - reasonably enough - and thus the object is wrong. The end result is that the ids field is set to ["1"] and not ["1", "2", "3"] as it should be.
So what I want to be able to do is fix this. And I'm thinking that the easiest/safest/best way is to be able to modify the JSON AST somewhere in the deserializing process. Specifically by just removing the "id" field from the JSON so that it doesn't get seen by the standard deserializer. (I know this works by doing it manually with string manipulation, but that's awful)
I could write a full deserializer for all the fields, but then I'm beholden to maintaining it if any new fields are added in the future, when all I actually want to do is ignore one single field and have everything else processed as normal. The real class actually has about a dozen fields at present, and I can't guarantee that it won't change in the future.
What I can't work out is how to do this. I was really hoping there was just some standard JsonDeserializer subclass that would let me do this, but I can't find one. The best I've been able to work out is a normal StdDeserializer that then uses parser.getCodec().treeToValue() - courtesty of this answer - except that this results in an infinite loop as it calls back into the exact same deserializer every time!
Frustratingly, most answers to this problem are "Just annotate the class" - and that's not an option here!
Is there a standard way to achieve this?
Cheers
There are the Jacksons mixins for exactly this case, a super useful feature!
For your case, define the mixin class and annotate it as if you were annotating the original; you only need to include the overrides, e.g.:
#JsonIgnoreProperties({ "id" })
public class ResultMixin {
// nothing else required!
}
Now, you will have to hook on the ObjectMapper creation to define the mixin. This depends on the framework you are using, but in the end it should look like this:
ObjectMapper om = ...
om.addMixIn(Result.class, ResultMixin.class);
Now this ObjectMapper will take into account the information from you mixin to serialize objects of type Result (and ignore the synthetic id property).
And, of course, I've just found out how to do it :)
I can use BeanDeserializerModifier instead to make things act as needed.
public class MyDeserializerModifier extends BeanDeserializerModifier {
#Override
public List<BeanPropertyDefinition> updateProperties(final DeserializationConfig config,
final BeanDescription beanDesc, final List<BeanPropertyDefinition> propDefs) {
if (beanDesc.getBeanClass().equals(Result.class)) {
propDefs.removeIf(prop -> prop.getName().equals("id"));
}
return super.updateProperties(config, beanDesc, propDefs);
}
#Override
public BeanDeserializerBuilder updateBuilder(final DeserializationConfig config, final BeanDescription beanDesc,
final BeanDeserializerBuilder builder) {
if (beanDesc.getBeanClass().equals(Result.class)) {
builder.addIgnorable("id");
}
return super.updateBuilder(config, beanDesc, builder);
}
}
This is the Java enum that I want to transform into an Avro Schema:
public enum ApplicationCode {
APP_A("MY-APP-A"),
APP_B("MY-APP-B");
private final String code;
ApplicationCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
Since enums are generally available as Types in Avro, I came up with following:
{
"type" : "enum",
"name" : "ApplicationCode",
"namespace" : "com.example",
"symbols" : [ "APP_A", "APP_B" ]
}
And referenced it in my main Avro like this:
"fields": [
{
"name": "masterApplicationCode",
"type": "ApplicationCode"
},
It works like that but unfortunately I am losing the Application Codes (e.g. "MY-APP-A") using this approach. I'm looking for something, that allows me to include both, the code and the label. Something like
{
"type" : "enum",
"name" : "ApplicationCode",
"namespace" : "com.example",
"symbols" : [ "APP_A("MY-APP-A")", "APP_B("MY-APP-B")" ]
}
Is it even possible to have this kind of complex enums or is there any workaround to achieve this?
I believe the avro schema is internally transforming it into a JSON String. So, I think the question is more about serializing enums. Reference from here - https://www.baeldung.com/jackson-serialize-enums
I think it should return the code if you use JsonFormat annotation like this -
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ApplicationCode {
Otherwise you will need to add a Custom Serializer for enum.
I solved my problem by writing custom serializer / deserializer that map an object with complex typed fields to one that is being sent with e.g. Strings instead of enums.
Here an example of the custom serializer:
public class CustomSerializer implements Serializer<ApplicationObject> {
#Override
public byte[] serialize(String topic, ApplicationObject ApplicationObjectDto) {
com.example.avro.ApplicationObject applicationObject = com.example.avro.ApplicationObject.newBuilder()
.setApplicationCode(ApplicationObjectDto.getApplicationCode().getCode())
.build();
return SerializationUtils.serialize(applicationObject);
}
}
I have a json which is complex/nested. My json file consists of two equivalent java objects. One is Complex_Expression and another is Simple_Expression.
Complex_Expression is in the following form:
{
"SomeOpearator":0,
"ASpecificKey":1, //the value 1 is fixed for complex expression.
"Expressions":[ ] //array of one or more expressions
}
Simple Expression is in the following form:
{
"Operand":"Some String Value",
"ASpecificKey":0, //the value 0 is fixed for simple expressions.
"SomeComparisionOpearator":1, // enums which associates int to different comparison operators.
"Value":["String1"] //this is an array of strings.
}
The Expressions in turn can have Complex_Expression and/or Simple_Expression. The json file always starts from Complex_Expression. I have to deserialize this JSON file. My final goal is to make an expression usingComplex_Expression and Simple_Expression objects and with some logics in these classes. I don't mind using jackson or gson or maybe other dependencies.
Till now I have created a base class called Expression. Complex_Expression and Simple_Expression both inherits this class. Then I started writing Custom Json Deserializer. But in the custom deserializer I am stuck and I don't know how should I proceed. Please help me on this. My Simple_Expression class looks like this and somewhat similar is the Complex_Expression class.
public class Simple_Expression extends Expression
{
#JsonProperty("Operand") //use jackson deserializer for this class.
public String Operand;
#JsonProperty("SomeComparisionOpearator")
public SomeComparisionOpearator someCompareOperator;
#JsonProperty("Value")
public Object value;
public Simple_Expression()
{
super(ASpecificKey.Simple); //Simple corresponds to 0
}
}
Update
Some more description about my input and output. With input given a JSON string like this:
{
"SomeOpearator": 0,
"ASpecificKey": 1,
"Expressions": [
{
"SomeOpearator": 1,
"ASpecificKey": 1,
"Expressions": [
{
"Operand": "People",
"ASpecificKey": 0,
"SomeComparisionOpearator": 14,
"Value": [
"Rich"
]
}
]
},
{
"SomeOpearator": 1,
"ASpecificKey": 1,
"Expressions": [
{
"Operand": "Grade",
"ASpecificKey": 0,
"SomeComparisionOpearator": 2,
"Value": [
"Grade A"
]
}
]
}
]
}
I should be able to do something like this, assuming jackson deserializer:
ObjectMapper mapper = new ObjectMapper();
Expression myExpressionObject = mapper.convertValue(jsonString, Expression.class);
It should give me the deserialized object into the myExpressionObject which will consists of a list of expressions (Arraylist or Array, no problem).
This would be easy with Gson extras RuntimeTypeAdapterFactory because you have the field ASpecificKey that can be used as type discriminator. See this for usage. You can just copy the source to your project if you already have Gson included.
I took a liberty to fix your Java naming convention so classes actually look like (and also your JSON should be fixed to correct convention):
#Getter #Setter
public class Expression {
private int aSpecificKey;
}
#Getter #Setter
public class SimpleExpression extends Expression {
public SimpleExpression() {
setASpecificKey(0);
}
private String operand;
private int someComparisonOperator;
private String[] values;
}
#Getter #Setter
public class ComplexExpression extends Expression {
public ComplexExpression() {
setASpecificKey(1);
}
private String someOperator;
private Expression[] expressions;
}
Against this kind of DTOs you could just instantiate a specific RuntimeTypeAdapterFactory:
final RuntimeTypeAdapterFactory<Expression> expressionTypeFactory =
RuntimeTypeAdapterFactory
.of(Expression.class, "aSpecificKey")
.registerSubtype(SimpleExpression.class, "0")
.registerSubtype(ComplexExpression.class, "1")
Then deserializing would be like:
Expression e = getGson(expressionTypeFactory)
.fromJson(getPackageResourceReader(YOUR_NOTATION_FIXED_JSON),
Expression.class);
Note that Gson will not by default deserialize the type discriminator aSpecificKey that is why there is default constructors that set that. If you do not need it you can remove the default constructors.
I have a json object which looks something like this
"price": {
"sale": {
"value": 39.99,
"label": "Now"
},
"regular": {
"value": 69.5,
"label": "Orig."
},
"shippingfee": 0,
"pricetype": "Markdown",
"pricetypeid": 7,
"onsale": true
}
"sale" and "regular" are keywords (unique) are 2 of the many price-types available, and the labels
"Orig" and "Now" are keywords (unique) are 2 of the many labels available.
I am not sure the best data structure to store this price json into the POJO.
can someone please guide me through this ?
I guess your problem is to convert the sale and regular attributes into an uniform representation which probably could use an Enumeration for the unique values. With default mechanism of JSON serilization/deserialization, this could be difficult. I am not sure about the JSON parsing library you are using, in Jackson there is a possibility to register custom serializers and deserializers for fields (using annotations). In this case, whcy tou would do is to register a custom serializer/deserializer and handle the fields of the JSON in the way you want it. You could refer this post
Added the below in response to the comment:
A probable dtructure for the POJO could be as below:
publi class Price{
protected List<PricePointDetails> pricePointDetails;
protected float shippingFee;
protected PriceTypes priceType;
protected priceTypeId;
protected boolean onSale;
}//class closing
public class PricePointDetails{
protected PriceTypes priceType;
protected float value;
protected LabelTypes labelType;
}//class closing
public enumeration PriceTypes{
sale,regular,markdown;
}//enumeration closing
public enumeration LabelTypes{
Orig,Now;
}//enumeration closing
What I have added, is just one way of structuring the data, it could be done in otherways also.
I'm currently having an issue with the deserialization of certain inner-objects, in spring, I initialize all of my objects before outputting them using #ResponseBody.
As an example, this is a response:
[{id:1, location:{id:1, ... extra location data}},
{id:2, location:1}
]
Now, GSON throws an error as it is not able to understand that location:1 refers to the location object already deserialized in the previous object.
Deserialization is done in the following method:
#Override
public void handleReader(Reader reader) {
try {
String json = readerToString(reader);
T object = getGson().fromJson(json, returnType);
handleObject(object);
} catch (Exception e) {
Sentry.captureException(e);
}
}
As an example, this is called through a regular generic class, I'd use the type Event[] as the T generic in order to return an array.
How can I either fix this using Gson or make spring output the full data every time? Ideally I'd like to fix with Gson as it would allow for seriously reduced bandwidth but I'm not too fussed at this point.
My Spring returning method is as follows:
#Override
public List<T> list() {
return service.findAll();
}
with the initialization like so:
#Override
#Transactional
public List<Event> findAll() {
List<Event> list = eventRepository.findByArchivedFalse();
for (Event event : list) {
this.initialize(event);
}
return list;
}
#Override
public Event initialize(Event obj) {
Hibernate.initialize(obj.getLocation());
Hibernate.initialize(obj.getLocation().get... inner data here);
return obj;
}
I imagine this is going to require a real structure review but, if I can help it, I'd like to keep the structure roughly the same.
You're going to have to write a custom deserializer, if you're not willing to change the JSon. However, changing the JSon is exactly what I would recommend.
Option 1: Changing the JSon
I think the right thing to do is to have two separate messages, e.g.
{
"uniqueLocations":
[
{"id":1, ... extra location details} ,
],
"locationMap":
[
{"id":1,"location":1},
{"id":2,"location":1}
... etc.
]
}
This is clearer; this separates your json so that you always have the same types of data in the same places.
Option 2: Making Gson able to do more complicated deserializations
However, if you're not willing to do that, you could write a custom deserializer. The most straightforward way to do that, extending TypeAdapter, only uses specific, concrete classes, not parameterized types. However, if you want to use a parameterized type, you must use a TypeAdapterFactory.
You can read more about how to do this here: How do I implement TypeAdapterFactory in Gson?