Handle Serialization and Deserialization with json schema using jsonschema2pojo - jackson

I have below json schema and generated java class using jsonschema2pojo library
AddressSchema.json
{
"$id": "https://example.com/address.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"description": "An address similar to http://microformats.org/wiki/h-card",
"type": "object",
"properties": {
"address": {
"type": "string"
}
}
AddressSchema.java
public class AddressSchema {
private String address;
#JsonProperty("address")
public String getAddress() {
return address;
}
#JsonProperty("address")
public void setAddress(String address) {
this.address = address;
}
}
My requirement is to generate class with different values in #JsonProperty on setter and getter like below. Is there any way to achieve this behavior?
public class AddressSchema {
private String address;
#JsonProperty("address")
public String getAddress() {
return address;
}
#JsonProperty("addr") //different value in the setter
public void setAddress(String address) {
this.address = address;
}
}

I believe you are trying to use the same class to parse a certain json with different name addr and have it return with different name address. I don't see how it might be possible without using two classes and a mapper to map the values. As humans we'd think address and addr are pretty similar and there must be mechanisms to map them while address and name are totally different I wouldn't ask for them to be mapped. But for computers it would be a difficult feature to provide. Hope you get my point.

Related

Cannot deserialize instance of MyEnum out of START_OBJECT token

Stuck with the problem of MyEnum enum deserialization from JSON to POJO and cannot figure out what I do wrong. So basically I try to retrieve some data calling particular microservice endpoint that returns the following json:
{
"id": "9cabf3e9-965d-4407-b62b-c57dd6006419",
"myEnums": [
{
"context": "SOME_FOO_CONTEXT_1",
"feature": "SOME_BAR_FEATURE_1",
"name": "SOME_FOO_BAR_1"
},
{
"context": "SOME_FOO_CONTEXT_2",
"feature": "SOME_BAR_FEATURE_2",
"name": "SOME_FOO_BAR_2"
}
],
"name": "Some name",
"updatedBy": null,
"updated": "2019-05-16T00:11:19.279Z"
}
This is the method that calls another microservice endpoint, deserialize response body to POJO and return result as Set:
private Mono<Set<MyEnum>> fetchMyEnums(UUID someId) {
return webClient.get().uri("/v1/something/{id}", someId)
.retrieve()
.bodyToMono(MyClass.class)
.flatMapIterable(MyClass::getMyEnums)
.collect(toSet());
}
The class that used for JSON deserialization:
#lombok.Value
static class MyClass {
List<MyEnum> myEnums;
}
Enum that I actually cannot deserialize:
#Getter
#RequiredArgsConstructor
#AllArgsConstructor
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum {
SOME_FOO_BAR_1(SOME_FOO_CONTEXT_1, SOME_BAR_FEATURE_1),
SOME_FOO_BAR_2(SOME_FOO_CONTEXT_2, SOME_BAR_FEATURE_2);
private final FooEnum context;
private final BarEnum feature;
private String name;
#JsonProperty
public String getName() {
return super.name();
}
}
During deserialization I receive the following exception:
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize instance of `com.learn.common.security.model.MyEnum` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `com.learn.common.security.model.MyEnum` out of START_OBJECT token
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.learn.common.security.service.MyEnumService$MyClass["myEnums"]->java.util.ArrayList[0])
Where I did mistake?
So spending few more hours to clarify what's the problem with deserialization i figure out that there is no automatic deserialization for Enum whose Shape.Object.
But I found workaround how to deserialize MyEnum object from json(you need define static method marked it as JsonCreator and define what input parameter you expect to catch from object defining JsonProperty with fieldName):
#Getter
#RequiredArgsConstructor
#AllArgsConstructor
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum {
SOME_FOO_BAR_1(SOME_FOO_CONTEXT_1, SOME_BAR_FEATURE_1),
SOME_FOO_BAR_2(SOME_FOO_CONTEXT_2, SOME_BAR_FEATURE_2);
private final FooEnum context;
private final BarEnum feature;
private String name;
#JsonProperty
public String getName() {
return super.name();
}
#JsonCreator
public static MyEnum fromJson(#JsonProperty("name") String name) {
return valueOf(name);
}
}
For sake of completeness: You can add the #JsonCreator annotation either to a constructor or to a factory method.
Constructor:
#JsonCreator
public MyEnum(#JsonProperty("name") String name) {
this.name = name;
}
Factory method:
#JsonCreator
public static MyEnum fromJson(#JsonProperty("name") String name) {
return valueOf(name);
}
Multiple parameters:
If your enum type contains multiple properties, add them to the signature with #JsonProperty annotation.
#JsonCreator
public MyEnum(#JsonProperty("id") String id, #JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
When using a factory method, creation from JSON might fail, if you have defined multiple properties. You may get this error message:
Unsuitable method [...] decorated with #JsonCreator (for Enum type [...])
Some versions of Jackson cannot handle this case. Use constructor method as a workaround, if your enum type contains more than one property.

Unwrap only some properties with Jackson

Assuming I have this objects:
class Person {
String name;
Household getHousehold();
}
class Household {
Set<Address> getAddresses();
String householdId;
}
which would normally be serialized as follows
{
"name": "XXX",
"household": {
"addresses": [...]
}
}
Is there a way to configure Jackson with annotations / mix-ins to obtain this (ie. without using DTO) ?
{
"name": "XXX",
"addresses": [...],
"household": {
"householdId": 123
}
}
You can configure the unwrapping of a specific property by both using mixins and annotations:
1. Mixins
Assuming you define the following mixin:
public abstract class UnwrappedAddresses {
#JsonUnwrapped
public abstract Household getHouseHold();
}
And then add a custom module to your objectMapper which applies the mixin to the Person class as follows:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper .registerModule(new SimpleModule() {
#Override
public void setupModule(SetupContext context) {
context.setMixInAnnotations(Person.class, UnwrappedAddresses.class);
}
});
This approach does not change the Household serialization as a single item, but just unwraps a household item when it's encapsulated in a Person object.
2. Annotations
Just add #JsonUnwrapped to your getHouseHold() method.
EDIT: After post changes.
What you want is basically to change the output of the json, which can be done by using the #JsonAnyGetter annotation(which can dynamically add new properties to your pojo).
Your expected result can be achieved by ignoring the household property and unwrapping it with the help of the #JsonAnyGetter.
#JsonIgnoreProperties("houseHold")
public static class Person {
String name;
Household houseHold;
#JsonAnyGetter
public Map<String,Object> properties(){
Map<String,Object> additionalProps=new HashMap<>();
additionalProps.put("addresses", new ArrayList<>(houseHold.getAddresses()));
Map<String,Object> houseHolProps=new HashMap<>();
houseHolProps.put("houseHoldId", houseHold.id);
additionalProps.put("houseHold", houseHolProps);
return additionalProps;
}
..getters&setters omitted
}
Which would after serialization return
{"name":"name",
"houseHold":{"houseHoldId":0},
"addresses":[
{"houseNo":2,"street":"abc"},
{"houseNo":1,"street":"str"}
]
}

SignalR and serializing object array

I'm new to SignalR and have done a simple test hack. I wish to serialize an object array with typed objects. By default SignalR has configured the JSon.NET serializer to not provide with type information. And I found that I could register a custom serializer in the DependencyResolver by:
var serializer =
new EventHubJsonSerializer(
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
TypeNameHandling = TypeNameHandling.Objects
});
GlobalHost.DependencyResolver.Register(typeof(IJsonSerializer), () => serializer);
However when I recieve my object array it will not resolve the types, instead it is a JSonContainer. Can I solve this in any way?
The event emitted from the Hub:
public sealed class SignalREvent
{
public string Group { get; set; }
public string EventName { get; set; }
public string TypeFullName { get; set; }
public IList<object> EventArguments { get; set; }
}
And the receptor have to unwrap the boolean via casting to dynamic:
public sealed class TestEventArgs : EventArgs
{
#region Public Properties
/// <summary>
/// Gets or sets a value indicating whether do not print.
/// </summary>
public bool DoNotPrint { get; set; }
/// <summary>
/// Gets or sets the event name.
/// </summary>
public string EventName { get; set; }
#endregion
}
this.subscription = this.client.On<SignalREvent>(
"PushEvent",
data =>
{
dynamic eventArg1 = data.EventArguments[0];
if (eventArg1.DoNotPrint.Value)
{
return;
}
});
What I've done is a postsharp aspect to apply on events in order to allow them to propagate via SignalR via my EventHub. For example:
[ExternalizeEvent]
public event ASimpleDelegate SimpleEvent;
It's a darn simple aspect, but it would really be good to have type info when in the .net world - other clients would of course not benefit of this.
Update
This is the output for my JSon.NET configuration - types are propagated in the $type but it seems that it is not used during deseralization.
{
"$id": "11",
"$type": "<>f__AnonymousType0`3[[System.String, mscorlib],[System.String, mscorlib],[System.Object[], mscorlib]], SignalR",
"Hub": "Externalize.EventHub",
"Method": "PushEvent",
"Args": [
{
"$id": "12",
"$type": "DataDuctus.SignalR.Aspects.SignalREvent, DataDuctus.SignalR.Aspects",
"Group": "all",
"EventName": "SimpleEvent",
"TypeFullName": "TestConsole.TestEvents",
"EventArguments": [
{
"$id": "13",
"$type": "TestConsole.TestEventArgs, TestConsole",
"DoNotPrint": false,
"EventName": "second event (test)"
}
]
}
]
}
Cheers,
Mario
The SignalR .NET client does not use the DependencyResolver from the server and currently does not have an IoC container of its own. Because of this, as you note in your question, your custom JsonSerializerSettings are used for serialization on the server but not for deserialization on the client.
In the next release of SignalR we plan to add a DependencyResolver to the .NET client that will allow you to provide your own Newtonsoft.Json.JsonSerializer or Newtonsoft.Json.JsonSerializerSettings to be used during deserialization. There are currently no plans to allow the use of a non-Json.NET (de)serializer in the .NET client.
If you need this functionality now, you could clone https://github.com/SignalR/SignalR.git and modify the private static T Convert<T>(JToken obj) method in SignalR.Client\Hubs\HubProxyExtensions.cs to return obj.ToObject<T>(yourJsonSerializer).

JSON.NET customizing the serialization to exclude a property name

I am using Json.NET and I have the following code.
public class Row
{
[JsonProperty]
public IDictionary<string, object> Cells { get; set; }
}
The rows and cells are dynamically generated and I had C# dynamic/Expando feature to create those Cells. When Json.NET serializes the dynamic instances, it produces the correct json structure I want. However for a large data structure it has an adverse effect on the performance. For example, JsonSerializer calls DynamicObject TryGetMember quite extensively during the serialization. Therefore I needed a static data structure so the serialization would be much quicker. The synamic Expando object would still create dynamic properties, but I wanted the Json.NET serialization to use the static structure (created based on the dynamic structure) so the serialization would be much quicker.
Cells dictionary get populated based on the dynamic structure, and by invoking the JsonConvert, it produces the serialized json structure as below.
string serializeObject = JsonConvert.SerializeObject(data, Formatting.Indented);
//json output:
{
"Data": [
{
"Cells": {
"column1": "20",
"column2": "20"
}
},
{
"Cells": {
"column1": "20",
"column2": "20"
}
}
]
}
However the UI Grid which I’m binding to, require the below json structure
"Data": [
{
"column1": "20",
"column2": "20"
},
{
"column1": "20",
"column2": "20"
}
]
Is there a way I could remove the “Cells” and produce the Json structure as above?
I looked at the JSON.NET help docos and I could not find a way to achieve this.
Also tried overriding the DynamicContractResolver’s CreateProperty method to see if I can change the serialization behaviour, but I was unable to do so
public class DynamicContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(System.Reflection.MemberInfo member, MemberSerialization memberSerialization)
{
if (member.Name == "Cells")
{
//remove the name "Cells" from the serialized structure
}
return base.CreateProperty(member, memberSerialization);
}
}
Or this is not simply supported? Any suggestions or directions much appreciated.
Found a way to get around this by creating a custom converter. The below code produces the Json result I need.
public class GridJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true; //TODO: more logic check the type before the conversion..
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
var rows = (List<Row>)value;
writer.WriteStartObject();
writer.WritePropertyName("data");
writer.WriteStartArray();
foreach (var row in rows)
{
writer.WriteStartObject();
var cells = row.Cells;
foreach (var cell in cells)
{
writer.WritePropertyName(cell.Key);
writer.WriteValue(cell.Value);
}
writer.WriteEndObject();
}
writer.Flush();
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
//Usage example:
string serializeObject = JsonConvert.SerializeObject
(someData, Formatting.Indented, new GridJsonConverter());

Custom datacontract / datamember name

I have a following problem. A customer requested a web service that returns data in following format:
<status>
<name1>Some name</name1>
...
</status>
But when an error occurs they want to get a following message:
<status>
<error>Error description</error>
</status>
I created a web service using WCF and in order to fulfill the requirements I defined a following service contract:
[ServiceContract]
public interface IPatronStatus
{
[OperationContract]
[ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
[WebGet(UriTemplate = "/service/status?user={unilogin}")]
StatusData GetPatronStatus(string unilogin);
}
And also defined a following base class:
[DataContract(Name="status")]
public class StatusData
{
}
And two derrived classes:
public class PatronStatusData : StatusData
{
private string _name;
[DataMember(Name = "name1", Order = 0)]
public string Name
{
get { return _name; }
set { _name = value; }
}
...
}
And:
public class UniLoginNotFoundError : StatusData
{
public UniLoginNotFoundError()
{ }
private string _description = "UniLoginNotFoundError";
[DataMember(Name = "error", Order = 0)]
public string Description
{
get
{
return _description;
}
}
}
The problem is that when I pull the data from the web service the data contract name ("status") and the names of the data members are ignored and the type's and properties' names are used.
Is it possible to use the custome names?
You should decorate both UniLoginNotFoundError and PatronStatusData with DataContract(Name="Something") to make this work. But you won't be allowed to set the same name ("status") for them.
In your particular case I'd better use single class with unused properties set to null.
[DataContract(Name="status")]
public class StatusData
{
private string _name;
private string _errorDescription = null;
[DataMember(Name = "name1", Order = 0, EmitDefaultValue=false)]
public string Name
{
get { return _name; }
set { _name = value; }
}
[DataMember(Name = "error", Order = 1, EmitDefaultValue=false)]
public string Description
{
get{ return _errorDescription ;}
set {_errorDescription =value ;}
}
...
}
Generally speaking, it's a mistake to want too much control over the XML generated by serializing a Data Contract. That's the trap of the XML Serializer. Define the contract in general terms, and have the clients just consume the result, which will generally be simple enough.