Jackson deserialization of interface affects constituent member's deserializaton - jackson

I have an interface type with many implementation classes. I can serialize/deserialize this either by
#JsonDeserialize(using = AlarmStateDeserializer.class)
#JsonSerialize(using = AlarmStateSerializer.class)
#JsonTypeInfo( use = JsonTypeInfo.Id.NONE)
public interface AlarmState
{
:
or
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
#JsonSubTypes({
#JsonSubTypes.Type(value = AcknowledgedState.class, name = "acknowledged"),
#JsonSubTypes.Type(value = ActivatedState.class, name = "activated"),
#JsonSubTypes.Type(value = DeactivatedState.class, name = "deactivated"),
#JsonSubTypes.Type(value = BlockedState.class, name = "blocked"),
#JsonSubTypes.Type(value = ReactivatedState.class, name = "reactivated"),
#JsonSubTypes.Type(value = DisabledState.class, name = "disabled"),
#JsonSubTypes.Type(value = NormalState.class, name = "normal"),
})
and that works fine when I seralize/deserialize the AlarmState directly in the ObjectMapper.
BUT, if I include the AlarmState as a property in another type, it insists to have a type for that field.
I have tried just about every other way I can think of, but for instance;
#JsonTypeInfo( use = JsonTypeInfo.Id.NONE )
#JsonDeserialize(as = AlarmImpl.class)
public interface Alarm
{
AlarmState getState();
:
(It doesn't matter if I deserialize the AlarmImpl directly either, same result)
And I will get the following exception;
Could not resolve subtype of [simple type, class com.sensetif.pipes.alarms.AlarmState]: missing type id property 'type' (for POJO property 'state')
I have also tried to put all kinds of annotations on the getState() method, but to no prevail.
FTR; the json in question is;
{
"labels": [],
"state": {
"name": "normal",
"description": "Normal state indicates everything is fine.",
"creationTime": "1970-01-01T00:00:00Z"
},
"counter": 0,
"name": "niclas1",
"description": null,
"condition": false,
"alarmClass": "C",
"organization": 0,
"parameters": null
}
How can it be that the underlying type deserializes fine, but not when it is part of an outer object??

Related

Problem to deserialize json to java object of generic type with jackson 2.13.4.2

There's a message defined as below, note there're different implementations of the generic type M
// The message definition
#Value
#Builder(toBuilder = true)
#Jacksonized
public class MyMessage<M> {
#Builder.Default
Map<String, String> props = new HashMap<>();
M content;
}
// One implementation of the generic type M in MyMessage
class ContentType1 {
String name;
SomeSimplePojo pojo;
Map<String, String> contentProps;
}
Here's an example of above message:
{
"props": {
"trace-id": "3468f6022b749dbc"
},
"content": {
"name": "contentExample1",
"pojo": {
"field1": "val1",
"field2": "val2"
},
"contentProps": {
"/Count": "9",
"/Email": "someone#stackoverflow.com"
}
}
}
The message was deserialized basically with below code snippet, by com.fasterxml.jackson.* version 2.10.4. It worked fine before.
ObjectMapper objectMapper = new ObjectMapper();
MyMessage<String> rawMsg = objectMapper.readValue(jsonStr, new TypeReference<MyMessage<String>>() {});
// Here clazzOfM is the class of type M
MyMessage<M> convertedMsg = MyMessage.<M>builder().content(objectMapper.convertValue(rawMsg.getContent(), clazzOfM)).props(rawMsg.getProps()).build();
But recently, I upgraded com.fasterxml.jackson.databind to 2.13.4.2, all other com.fasterxml.jackson.* to 2.13.4. Then it failed at this line MyMessage<String> rawMsg = objectMapper.readValue(jsonStr, new TypeReference<MyMessage<String>>() {}); with Exception, which points to the generic type M, field content at column 52:
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type `java.lang.String` from Object value (token `JsonToken.START_OBJECT`)
at [Source: (String)"{"props":{"trace-id":"3468f6022b749dbc"},"content":{"name":"contentExample1","pojo":{"field1":"val1","field2":"val2"},"contentProps":{"/Count":"9","/Email":"someone#stackoverflow.com"}}}"; line: 1, column: 52] (through reference chain: com.demo.example.MyMessage$MyMessageBuilder["content"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1741)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1515)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1420)
at com.fasterxml.jackson.databind.DeserializationContext.extractScalarFromObject(DeserializationContext.java:932)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:62)
at com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeSetAndReturn(MethodProperty.java:158)
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.vanillaDeserialize(BuilderBasedDeserializer.java:293)
at com.fasterxml.jackson.databind.deser.BuilderBasedDeserializer.deserialize(BuilderBasedDeserializer.java:217)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.readRootValue(DefaultDeserializationContext.java:323)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4674)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3629)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3612)
I tried to change the problematic line to MyMessage<M> rawMsg = objectMapper.readValue(jsonStr, new TypeReference<MyMessage<M>>() {});, no exception then, but I still have two questions.
Is the above change a correct approach to deserialize json text to MyMessage by jackson 2.13.4(.2)? What is the best practice to this kind of deserialization if the above isn't.
After the above change, I notice that the type of rawMsg.content is LinkedHashMap, it isn't M(CotentType1 in this test) as I expected. But the type of convertedMsg.content IS ContentType1 after executing this converting line MyMessage<M> convertedMsg = MyMessage.<M>builder().content(objectMapper.convertValue(rawMsg.getContent(), clazzOfM)).props(rawMsg.getProps()).build();.
I can't understand why the type of rawMsg.content is LinkedHashMap instead of ContentType1. Could someone help explain?

Failed to denormalize attribute "date" value for class

I am trying to save by API an object given by the Front in React JS
So I have this object testing in Insomnia:
{
"rate": 1.59,
"correction":"2 ui nOVORAPID",
"date": "2020-11-26",
"time": "7:30"}
I don't understand why i have the error
Failed to denormalize attribute "date" value for class "App\Entity\Bloodsugar": Expected argument of type "string", "object" given at property path "date".
My controller:
$user = $this->getUser();
$jsonReceived = $request->getContent();
$json = json_decode($jsonReceived);
$newBloodsugar = $serializer->deserialize($jsonReceived, BloodSugar::class, 'json');
...
I guess that Symfony does not recognize the date format "Y-m-d", how can I do so ?
I guess your BloodSugar class has invalid setter or property type. Normally symfony-serializer normalize dates to Datetime, while your entity is expecting string. Try to change it to DatetimeInterface, smth like this:
class BloodSugar {
//..
private ?DatetimeInterface $date;
//..
public function setDate(DatetimeInterface $date){
$this->date = $date;
return $this;
}
//..
}

How to add custom serialization/deserialization for protobuf?

I have my message definition like
message ID {
string value = 1;
}
message User {
ID id = 1;
google.protobuf.StringValue name = 2;
}
Now if I serialize an instance of User to json, I get something like this
{
"id": {
"value" : "myid"
}
"name" : "Josh"
}
As you can see for the WKT types the value is unnested. However, for my custom message type User the value is nested. How do I make the output look like
{
"id": "myid"
"name" : "Josh"
}
I mean how do I serialize, deserialize to custom type.
One option I could think of is update this function https://github.com/protocolbuffers/protobuf/blob/master/python/google/protobuf/json_format.py#L199
This means, have a copy of json_format.py and extend _IsWrapperMessage to my custom types

Swagger-ui is not showing control documentation

I am trying to use springfox-swagger-ui for the documentation of my rest services. I had used the next basic configuration in a kotlin project:
This is my Docket class:
#Configuration
#EnableSwagger2
open class SwaggerConfig {
#Bean
open fun newsApi(): Docket {
return Docket(DocumentationType.SWAGGER_2)
.groupName("api-infos")
.apiInfo(apiInfo())
.directModelSubstitute(LocalDateTime::class.java, Date::class.java)
.select()
.paths(regex("/api.*"))
.build()
}
private fun apiInfo(): ApiInfo {
return ApiInfoBuilder()
.title("Infos REST api")
.description("Swagger test for Api ESPN")
.termsOfServiceUrl("http://en.wikipedia.org/wiki/Terms_of_service")
.contact("rodolfo.silva#globant.com")
.license("Apache License Version 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.version("1.0")
.build()
}
}
And this is my controller:
#Controller
#ProductApi(
id = "v1_browse_player",
title = "Browse Player (v1)",
description = "")
#Api(value = "controller", description = "Controllers API", produces = "application/json")
#RequestMapping("/api/infos")
class BrowsePlayerController {
#Autowired
lateinit var browsePlayerService: BrowsePlayerServiceRepresentable
#GetRequest(
path = "/v1/browse/players",
timeToLive = 300,
queries = [
QueryParameter(name = "swid", required = true),
QueryParameter(name = "uid"),
QueryParameter(name = "seeAll", type = java.lang.Boolean::class),
QueryParameter(name = "lang", required = true),
QueryParameter(name = "region", required = true),
QueryParameter(name = "version", required = true, type = Integer::class),
QueryParameter(name = "appName", required = true),
QueryParameter(name = "platform", required = true)
]
)
#ApiOperation(value = "Get the players", notes = "Returns one info for playerBrowse")
fun processBrowsePlayerRequest(transaction: Transaction, apiRequest: ApiRequest): Single<BrowsePlayerResponse?> {
val applicationContext = RequestBasedApplicationContext(apiRequest)
val standardContext = RequestBasedStandardContext(
RequestBasedVersionContext(apiRequest),
applicationContext,
RequestBasedEditionContext(apiRequest, applicationContext),
RequestBasedPlatformContext(apiRequest),
transaction
)
val swidContext = RequestBasedSWIDContext(apiRequest)
val uidContext = if (checkUIDPresent(apiRequest)) RequestBasedUIDContext(apiRequest) else null
val seeAllContext = RequestBasedSeeAllContext(apiRequest)
val requestBrowsePlayerContext = RequestBrowsePlayerContext(standardContext, swidContext, uidContext, seeAllContext, apiRequest)
return browsePlayerService.getEntitiesBrowse(requestBrowsePlayerContext)
}
private fun checkUIDPresent(apiRequest: ApiRequest): Boolean =
apiRequest.parameters["uid"] != null
}
I had used a very basic configuration, the ApiOperation, Api and RequestMapping("/api/infos") tags, also at the data class level, the next configuration:
#JsonInclude(JsonInclude.Include.NON_NULL)
data class TopBrowsePlayerHeader(val title: String, val searchURL: String?)
#ApiModel(value = "Info entity", description = "Entity class BrowsePlayerResponse")
data class BrowsePlayerResponse(
#ApiModelProperty(value = "The header of the info", required = false)
val header: TopBrowsePlayerHeader,
#ApiModelProperty(value = "The analytics node of the info", required = true)
val analytics: Analytics,
#ApiModelProperty(value = "The sections node of the info", required = true)
val sections: List<Section>)
When I load the http://localhost:8080/swagger-ui.html#/api-controller (swagger browser). I can't see my controller structure. It seems like there is a predefined endpoint which is shown:
http://localhost:8080/v2/api-docs?group=api-infos
I am not pretty familiar with this configuration. Any ideas to the correct configuration?
Thanks
Try to replace paths value with PathSelectors.any():
#Bean
open fun newsApi() : Docket {
return Docket(DocumentationType.SWAGGER_2)
.groupName("api-infos")
.apiInfo(apiInfo())
.directModelSubstitute(LocalDateTime::class.java, Date::class.java)
.select()
.paths(PathSelectors.any())
.build()
}
The default value for swagger path is /v2/api-docs.
You can change is in the application.properties with the springfox.documentation.swagger.v2.path key to whatever you want to.
?group=api-infos comes from the .groupName("api-infos") value.
If you don't want to group your APIs for any reasons (like sets of selected APIs for specific clients) then remove the .groupName(...).

Using jackson mixin class for a list of objects

I'm having a problem deserializing the following json
{
"GrpHdr": {
"MsgId": "Message-1",
"CreDtTm": "2018-03-02T10:15:30+01:00[Europe/Paris]",
"NbOfTxs": "1",
"InitgPty": {
"Nm": "Remitter"
}
},
"PmtInf": [
{
"PmtInfId": "1"
},
{
"PmtInfId": "2"
}
]
}
I have created a MixIn class:
public abstract class CustomerCreditTransferInitiationMixIn {
public PaymentInstructions paymentInstructions;
#JsonCreator
public CustomerCreditTransferInitiationMixIn(
#JsonProperty("GrpHdr") GroupHeader GrpHdr,
#JsonProperty("PmtInf") List<PaymentInstruction> PmtInf
) {
this.paymentInstructions = PaymentInstructions.valueOf(PmtInf);
}
#JsonProperty("GrpHdr")
abstract GroupHeader getGroupHeader();
#JsonProperty("PmtInf")
abstract List<PaymentInstruction> getPaymentInstructions();
}
I'm having no trouble deserializing the group header in this case. Mapping different names. But in the PmtInf case I get confused. It is a list that I want to deserialize to a List of PaymentInstructions. But PmtInf is a paymentistruction.
I have created a test:
#Test
public void JacksonMixinAnnotationTestJsonIsoFileFromTester() throws JsonProcessingException, Throwable {
CustomerCreditTransferInitiation customerCreditTransferInitiation;
String jsonFile = "testWithShortNames";
InputStream inputStream = new ClassPathResource(jsonFile + ".json").getInputStream();
ObjectMapper objectMapper = buildMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.registerModule(new JavaTimeModule());
objectMapper.addMixIn(CustomerCreditTransferInitiation.class, CustomerCreditTransferInitiationMixIn.class);
objectMapper.addMixIn(GroupHeader.class, GroupHeaderMixIn.class);
objectMapper.addMixIn(PaymentInstruction.class, PaymentInstructionMixIn.class);
objectMapper.addMixIn(PartyIdentification.class, PartyIdentificationMixIn.class);
customerCreditTransferInitiation = objectMapper.readValue(inputStream, CustomerCreditTransferInitiation.class);
//GroupHeader
Assert.assertNotNull(customerCreditTransferInitiation.getGroupHeader());
Assert.assertNotNull(customerCreditTransferInitiation.getGroupHeader().getMessageId());
Assert.assertNotNull(customerCreditTransferInitiation.getGroupHeader().getCreationDateTime());
Assert.assertNotNull(customerCreditTransferInitiation.getGroupHeader().getNumberOfTransactions());
Assert.assertNotNull(customerCreditTransferInitiation.getGroupHeader().getInitiatingParty());
Assert.assertNotNull(customerCreditTransferInitiation.getGroupHeader().getInitiatingParty().getName());
//PaymentInstructions
Assert.assertNotNull(customerCreditTransferInitiation.getPaymentInstructions());}
Getting the following error:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:
Unrecognized field "PmtInfId" (class
com.seb.payment.iso.domain.PaymentInstruction), not marked as
ignorable (19 known properties: "paymentInformationId",
"paymentMethod", "created", "paymentTypeInformation", "controlSum",
"debtorAgent", "instructionForDebtorAgent", "numberOfTransactions",
"requestExecutionTime", "debtorAccount", "creditTransferTransactions",
"debtorAgentAccount", "batchBooking", "poolingAdjustmentDate",
"ultimateDebtor", "chargeBearerType", "debtor", "chargesAccount",
"chargesAccountAgent"]) at [Source: UNKNOWN; line: -1, column: -1]
(through reference chain:
com.seb.payment.iso.domain.CustomerCreditTransferInitiation["PmtInf"]->com.seb.payment.iso.domain.PaymentInstruction["PmtInfId"])
In our case we have implemented our own deserializers in abstract iterable.
On:
ObjectReader objectReader = ObjectMapperFactory.instance().readerFor(this.itemClass);
MixedIn classes are lost