Enum Keys are discarded on Deserialization - jackson

I am trying to deserialize the following json structure to object.
"policyDetail": {
"policies": {
"API_KEY": {
"isEnabled": "Yes",
"policyEnabled": true
},
"BASIC_AUTH": {
"username": "username",
"password": "password",
"policyEnabled": true
}
}
}
In this structure, API_KEY and BASIC_AUTH are java Enum type. I am trying to deserialize like this.
In Service class
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
SimpleModule module = new SimpleModule();
module.addDeserializer(Policy.class, new PolicyDeserializer());
mapper.registerModule(module);
PolicyDeserializer.java
public class PolicyDeserializer extends StdDeserializer{
public PolicyDeserializer() {
super(Policy.class);
}
protected PolicyDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Policy deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = jp.readValueAsTree();
// Here I am only getting values under API_KEY not the entire API_KEY and underneath structure.
JsonNode customField = node.findValue("API_KEY");
Policy result = null;
if(customField != null && !customField.isNull()) {
ApiKeyPolicy apiKeyPolicy = new ApiKeyPolicy();
apiKeyPolicy = mapper.readValue(customField.toString(), ApiKeyPolicy.class);
result = apiKeyPolicy;
return result;
}
return result;
}
}
Policy.java
package model.policy;
import enums.PolicyType;
public abstract class Policy {
private PolicyType policyType;
private boolean isPolicyEnabled;
public Policy(PolicyType policyType) {
this.policyType = policyType;
}
/**
* #return the isPolicyEnabled
*/
public boolean isPolicyEnabled() {
return isPolicyEnabled;
}
/**
* #param isPolicyEnabled
* the isPolicyEnabled to set
*/
public void setPolicyEnabled(boolean isPolicyEnabled) {
this.isPolicyEnabled = isPolicyEnabled;
}
}
ApiKeyPolicy.java
import enums.PolicyType;
import model.Policy;
public class ApiKeyPolicy extends Policy {
private String isEnabled;
public ApiKeyPolicy() {
super(PolicyType.API_KEY);
}
/**
* #return the isEnabled
*/
public String getIsEnabled() {
return isEnabled;
}
/**
* #param isEnabled the isEnabled to set
*/
public void setIsEnabled(String isEnabled) {
this.isEnabled = isEnabled;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "ApiKeyPolicy [isEnabled=" + isEnabled + "]";
}
}
PolicyDetail.java
import java.util.EnumMap;
import java.util.Map;
import enums.PolicyType;
public class PolicyDetail {
EnumMap<PolicyType, Policy> policyMap = null;
public PolicyDetail() {
if(policyMap == null) {
policyMap = new EnumMap<PolicyType,Policy>(PolicyType.class);
}
}
public void addPolicy(PolicyType policyType, Policy policy, boolean isEnabled) {
if(null != policy) {
policy.setPolicyEnabled(isEnabled);
this.policyMap.put(policyType, policy);
}
}
public Map<PolicyType, Policy> getPolicies(){
return this.policyMap;
}
/* (non-Javadoc)
* #see java.lang.Object#toString()
*/
#Override
public String toString() {
return "PolicyDetail [policyMap=" + policyMap + "]";
}
}
As this is how 3rd party Json structure. Hence, I can not change the existing structure. Can you please suggest where am I missing?
Thanks in advance.. !

After too much of research and hit-and-trial. I found that the Parent Abstract class Policy.java should have following entries like
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.PROPERTY,
property = "type",
visible = true
)
#JsonSubTypes({
#JsonSubTypes.Type(value = APIKeyPolicy.class, name = "API_KEY"),
#Type(value = BasicAuth.class, name = "BASIC_AUTH")})
public abstract class Policy {
//with all methods
}
So, now when I would try to deserialize this to object I should be getting following
"API_KEY": {
"type": "API_KEY",
"isEnabled": "Yes",
"policyEnabled": true
},
"BASIC_AUTH": {
"type": "BASIC_AUTH",
"username": "username",
"password": "password",
"policyEnabled": true
}
But it comes with an additional property as type which is fine as of now. as I am more interested on Policy Name ENUM with corresponding attributes.

Related

Value Dependent Deserialization with Jackson

I want to deserialize into a data structure. Dependent on the version of the JSON data I want to deserialize into different implementations of the same interface. And this works so far with a custom deserializer.
However, in the data structure I use references. And I expect that when undefined references are encountered an exception is thrown. The way I programmed it, this does not work together with the interface.
I created a small example with a (currently not passing) test case to show the desired behavior.
Additional Information:
In the test case, when I use concrete classes (instead of the interface) in readValue the desired behavior occurs. That is, when I write mapper.readValue(buggy, Database2.class); instead of mapper.readValue(buggy, DatabaseI.class);. But then I lose the ability to abstract from the particular content of the JSON data.
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.btc.adt.pop.scen.objectstreams.Person;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.IntNode;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
public class Example {
#Test
public void test() throws JsonProcessingException {
ObjectMapper mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
SimpleModule module = new SimpleModule();
module.addDeserializer(DatabaseI.class, new ToyDeserializer());
mapper.registerModule(module);
String correct = "{'version':1,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['b']}]}";
DatabaseI deserCorrect = mapper.readValue(correct, DatabaseI.class);
System.out.println(mapper.writeValueAsString(deserCorrect));
String buggy = "{'version':2,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['FOO']}]}";
assertThrows(Exception.class, () -> {
mapper.readValue(buggy, DatabaseI.class);
}, "The reference FOO is undefined. An Exception should be thrown.");
}
}
class Person {
#JsonProperty("id")
private String id;
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id")
#JsonIdentityReference(alwaysAsId = true)
private List<Person> friends = new ArrayList<>();
public Person() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Person> getFriends() {
return friends;
}
public void setFriends(List<Person> friends) {
this.friends = friends;
}
}
interface DatabaseI {
}
class Database1 implements DatabaseI {
private int version;
private List<Person> people = new ArrayList<>();
public Database1() {
}
public List<Person> getPeople() {
return people;
}
public void setPeople(List<Person> people) {
this.people = people;
}
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
class Database2 implements DatabaseI {
private String version;
private List<Person> people = new ArrayList<>();
public Database2() {
}
public List<Person> getPeople() {
return people;
}
public void setPeople(List<Person> people) {
this.people = people;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
class ToyDeserializer extends StdDeserializer<DatabaseI> {
protected ToyDeserializer(Class<?> vc) {
super(vc);
}
public ToyDeserializer() {
this(null);
}
#Override
public DatabaseI deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JacksonException {
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode node = mapper.readTree(jp);
int version = (Integer) ((IntNode) node.get("version")).numberValue();
if (version == 1) {
return mapper.treeToValue(node, Database1.class);
} else {
return mapper.treeToValue(node, Database2.class);
}
}
}
This very good question! If you want to understand why no exception is thrown, your class Person must look like this:
#JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Person.class,
resolver = SimpleObjectIdResolverThrowsException.class
)
#JsonIdentityReference
class Person {
String id;
List<Person> friends = new ArrayList<>();
#ConstructorProperties({"id"})
public Person(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Person> getFriends() {
return friends;
}
public void setFriends(List<Person> friends) {
this.friends = friends;
}
}
class SimpleObjectIdResolverThrowsException extends SimpleObjectIdResolver {
public SimpleObjectIdResolverThrowsException() {
super();
}
#Override
public Object resolveId(ObjectIdGenerator.IdKey id) {
if (this._items == null) {
return null;
}
Object obj = this._items.get(id);
if (obj == null) {
throw new RuntimeException("Unresolved reference for: " + id);
}
return obj;
}
#Override
public ObjectIdResolver newForDeserialization(Object context) {
return new SimpleObjectIdResolverThrowsException();
}
}
Now you can set break point in the method resolveId and see what happens when we de-serialize the string "{'version':1,'people':[{'id':'a','friends':['b','c']},{'id':'b','friends':['c']},{'id':'c','friends':['b']}]}":
The problem is that the objects are processed one after the other and the references from the friends list are not resolved at that time.

How to return ObjectNode from Controller in Micronaut?

I have following code
#Controller()
public class TestController {
#Get(value = "test", produces = MediaType.APPLICATION_JSON)
public MyDto fetch() throws Exception {
return new MyDto(
"test",
new ObjectMapper().readValue("{\"a\": 1}", ObjectNode.class)
);
}
#Serializable
#Data
public static class MyDto {
private final String name;
private final ObjectNode extraFields;
public MyDto(String name, ObjectNode extraFields) {
this.name = name;
this.extraFields = extraFields;
}
}
}
And I have an unexpected output on the client, extraFields object is empty
{
"name": "test",
"extraFields": [
[]
]
}
How to make Micronaut controller properly serialize com.fasterxml.jackson.databind.node.ObjectNode ?

Why this constructor is not intercepted by my code?

I want to write a javaagent using byte-buddy. I wan to intercept constructor of any class that is a sub class of java.sql.Connection.
my setup code:
log.info("{} profile activated", profile);
if (profile.equals("hikari")) {
startMatcher = nameStartsWith("com.zaxxer.hikari");
JDBCAPIInterceptor.profile = new HikariProfile();
} else if (profile.equals("dbcp1")) {
startMatcher = nameStartsWith("org.apache.commons.dbcp");
JDBCAPIInterceptor.profile = new DBCP1Profile();
} else if (profile.equals("dbcp2")) {
startMatcher = nameStartsWith("org.apache.commons.dbcp2");
JDBCAPIInterceptor.profile = new DBCP2Profile();
} else if (profile.equals("druid")) {
startMatcher = nameStartsWith("com.alibaba.druid.pool");
} else {
startMatcher = any();
}
new AgentBuilder.Default().type(startMatcher.and(isSubTypeOf(java.sql.Connection.class).or(isSubTypeOf(java.sql.Statement.class))))
.transform(constructorTransformer).transform(methodsTransformer).with(listener).installOn(inst);
but why constructor of org.apache.commons.dbcp2.PoolingDataSource$PoolGuardConnectionWrapper can't be intercepted while it's definitely a sub class of java.sql.Connection as it extends org.apache.commons.dbcp2.DelegatingConnection which implements java.sql.Connection?
also, my intercepting code is executed while constructor of org.apache.commons.dbcp2.DelegatingConnection being called.
public class PoolingDataSource<C extends Connection> implements DataSource, AutoCloseable {
/**
* PoolGuardConnectionWrapper is a Connection wrapper that makes sure a closed connection cannot be used anymore.
*
* #since 2.0
*/
private class PoolGuardConnectionWrapper<D extends Connection> extends DelegatingConnection<D> {
PoolGuardConnectionWrapper(final D delegate) {
super(delegate);
}
#Override
public void close() throws SQLException {
if (getDelegateInternal() != null) {
super.close();
super.setDelegate(null);
}
}
/**
* #see org.apache.commons.dbcp2.DelegatingConnection#getDelegate()
*/
#Override
public D getDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getDelegate() : null;
}
/**
* #see org.apache.commons.dbcp2.DelegatingConnection#getInnermostDelegate()
*/
#Override
public Connection getInnermostDelegate() {
return isAccessToUnderlyingConnectionAllowed() ? super.getInnermostDelegate() : null;
}
#Override
public boolean isClosed() throws SQLException {
return getDelegateInternal() == null || super.isClosed();
}
}
}

orika property expression mapping

Given
classA {
long fahr;
....
and
classB {
long cels;
....
how can I map the following in Orika?
A.fahr <-> (B.cels*9)/5
Do I need customised Mapper or Filter ?
I suggest to use field level converter if both are of different data types but since they are of same data type we have to use a custom converter for entire class.
This is sample converter that suitable for this use case.
import ma.glasnost.orika.BoundMapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.converter.ConverterFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
public class EntryClass {
public static void main(String[] args) {
EntryClass ec = new EntryClass();
BoundMapperFacade<A, B> facade = getMapperFactory().getMapperFacade(A.class, B.class);
A fahr = new A(455);
B cels = facade.map(fahr);
System.out.println(cels);
A revFahr = facade.mapReverse(cels);
System.out.println(revFahr);
}
private static MapperFactory getMapperFactory() {
MapperFactory factory = new DefaultMapperFactory.Builder()
.build();
ConverterFactory cfactory = factory.getConverterFactory();
cfactory.registerConverter(new FahrCelsConverter());
factory.classMap(A.class, B.class)
.field("fahr", "cels")
.byDefault()
.register();
return factory;
}
}
public class A {
long fahr;
public A(long fahr) {
this.fahr = fahr;
}
public long getFahr() {
return fahr;
}
public void setFahr(long fahr) {
this.fahr = fahr;
}
#Override
public String toString() {
return "A [fahr=" + fahr + "]";
}
}
public class B {
long cels;
public B(long cels) {
this.cels = cels;
}
public long getCels() {
return cels;
}
public void setCels(long cels) {
this.cels = cels;
}
#Override
public String toString() {
return "B [cels=" + cels + "]";
}
}
public class FahrCelsConverter extends BidirectionalConverter<A, B>
{
#Override
public B convertTo(A source, Type<B> destinationType, MappingContext mappingContext) {
if(source != null)
{
return new B((source.fahr - 32) * 5 / 9);
}
return null;
}
#Override
public A convertFrom(B source, Type<A> destinationType, MappingContext mappingContext) {
if(source != null)
{
return new A((source.cels / 5) * 9 + 32);
}
return null;
}
}
It's more suited to use a converter (by id).

Value type field required in Razor View

I have an enum type field called Title.
[Serializable]
public enum Title
{
NotSet,
Miss = 4,
Mr = 1,
Mrs = 3,
Ms = 2
}
I want to bind a property of type Title to the Razor View but I don't want it to be a required field. However, on tabbing out or OnBlur, it is showing as required, although I have not specified this as required.
Is there any way I can get around this?
create
namespace YourApplicationName.Helper
{
public class ModelValueListProvider : IEnumerable<SelectListItem>
{
List<KeyValuePair<string, string>> innerList = new List<KeyValuePair<string, string>>();
public static readonly ModelValueListProvider TitleList = new TitleListProvider();
protected void Add(string value, string text)
{
string innerValue = null, innerText = null;
if (value != null)
innerValue = value.ToString();
if (text != null)
innerText = text.ToString();
if (innerList.Exists(kvp => kvp.Key == innerValue))
throw new ArgumentException("Value must be unique", "value");
innerList.Add(new KeyValuePair<string, string>(innerValue, innerText));
}
public IEnumerator<SelectListItem> GetEnumerator()
{
return new ModelValueListProviderEnumerator(innerList.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private struct ModelValueListProviderEnumerator : IEnumerator<SelectListItem>
{
private IEnumerator<KeyValuePair<string, string>> innerEnumerator;
public ModelValueListProviderEnumerator(IEnumerator<KeyValuePair<string, string>> enumerator)
{
innerEnumerator = enumerator;
}
public SelectListItem Current
{
get
{
var current = innerEnumerator.Current;
return new SelectListItem { Value = current.Key, Text = current.Value };
}
}
public void Dispose()
{
try
{
innerEnumerator.Dispose();
}
catch (Exception)
{
}
}
object System.Collections.IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
return innerEnumerator.MoveNext();
}
public void Reset()
{
innerEnumerator.Reset();
}
}
private class TitleListProvider : ModelValueListProvider
{
public TitleListProvider (string defaultText = null)
{
if (!string.IsNullOrEmpty(defaultText))
Add(string.Empty, defaultText);
Add(Title.NotSet, "NotSet");
Add(Title.Miss , "Miss");
Add(Title.Mr , "Mr");
Add(Title.Mrs , "Mrs");
Add(Title.MS, "MS");
}
public void Add(Title value, string text)
{
Add(value.ToString("d"), text);
}
}
}
}
in your model
public Title? Titleformation { get; set; }
public string[] SelectedTitle { get; set; }
in your view, also add the name space to your view
#using YourApplicationName.Helper;
#Html.ListBoxFor(m => m.SelectedTitle , new SelectList(ModelValueListProvider.TitleList, "Value", "Text"))
hope this help you
Enums require values, and cannot be null (aka not set) despite what someone commented above. What I do for salutations is have a "none" member of the enum, and whenever I print this out, I just check in the code to see if the value of the enum is > 0 (aka, the none option) and don't print it.
public enum Salutation { none,
[Description("Mr.")] Mr,
[Description("Mrs.")] Mrs,
[Description("Ms.")]Ms,
[Description("Miss")] Miss }
Use a class rather than enum ie:
public class Title
{
NotSet;
Miss = 4;
Mr = 1;
Mrs = 3;
Ms = 2;
}