Why do not #JsonTypeInfo work together with #JsonIdentityInfo? - jackson

#JsonIdentityInfo works as expected with the following classes:
Baseclass:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid")
public class TestEntityBas {
#JsonProperty
public String uuid = "0001";
}
Subclass:
public class TestEntityGoa extends TestEntityBas {
#JsonProperty
public String texten = "This is text!";
}
Container class:
public class TestEntity {
#JsonProperty
String stringer = "Hej hopp!";
#JsonIdentityReference(alwaysAsId = true)
public TestEntityGoa goa = new TestEntityGoa();
}
The result is as expected:
{"stringer":"Hej hopp!","goa":"0001"}
When I add #JsonTypeInfo to the base class like this:
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="#class")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "uuid")
public class TestEntityBas {
#JsonProperty
public String uuid = "0001";
}
Now the entire TestEntityGoa get serialized like this:
{"stringer":"Hej hopp!","goa":{"#class":"com.fodolist.model.TestEntityGoa","uuid":"0001","texten":"This is text!"}}
I expect the first result even when I use #JsonTypeInfo and #JsonIdentityInfo in the same class. What am I doing wrong?

I can't see anything obviously wrong here, so you may have found a bug. Combination of type and identity info is bit tricky to handle so there may be edge cases that do not yet work as intended, so could you file a bug at Github issue tracker for this?

Related

HTTP end point property string starts with "is" will get omit [duplicate]

This might be a duplicate. But I cannot find a solution to my Problem.
I have a class
public class MyResponse implements Serializable {
private boolean isSuccess;
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
Getters and setters are generated by Eclipse.
In another class, I set the value to true, and write it as a JSON string.
System.out.println(new ObjectMapper().writeValueAsString(myResponse));
In JSON, the key is coming as {"success": true}.
I want the key as isSuccess itself. Is Jackson using the setter method while serializing? How do I make the key the field name itself?
This is a slightly late answer, but may be useful for anyone else coming to this page.
A simple solution to changing the name that Jackson will use for when serializing to JSON is to use the #JsonProperty annotation, so your example would become:
public class MyResponse implements Serializable {
private boolean isSuccess;
#JsonProperty(value="isSuccess")
public boolean isSuccess() {
return isSuccess;
}
public void setSuccess(boolean isSuccess) {
this.isSuccess = isSuccess;
}
}
This would then be serialised to JSON as {"isSuccess":true}, but has the advantage of not having to modify your getter method name.
Note that in this case you could also write the annotation as #JsonProperty("isSuccess") as it only has the single value element
I recently ran into this issue and this is what I found. Jackson will inspect any class that you pass to it for getters and setters, and use those methods for serialization and deserialization. What follows "get", "is" and "set" in those methods will be used as the key for the JSON field ("isValid" for getIsValid and setIsValid).
public class JacksonExample {
private boolean isValid = false;
public boolean getIsValid() {
return isValid;
}
public void setIsValid(boolean isValid) {
this.isValid = isValid;
}
}
Similarly "isSuccess" will become "success", unless renamed to "isIsSuccess" or "getIsSuccess"
Read more here: http://www.citrine.io/blog/2015/5/20/jackson-json-processor
Using both annotations below, forces the output JSON to include is_xxx:
#get:JsonProperty("is_something")
#param:JsonProperty("is_something")
When you are using Kotlin and data classes:
data class Dto(
#get:JsonProperty("isSuccess") val isSuccess: Boolean
)
You might need to add #param:JsonProperty("isSuccess") if you are going to deserialize JSON as well.
EDIT: If you are using swagger-annotations to generate documentation, the property will be marked as readOnly when using #get:JsonProperty. In order to solve this, you can do:
#JsonAutoDetect(isGetterVisibility = JsonAutoDetect.Visibility.NONE)
data class Dto(
#field:JsonProperty(value = "isSuccess") val isSuccess: Boolean
)
You can configure your ObjectMapper as follows:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
&& method.getName().startsWith("is")) {
return method.getName();
}
return super.nameForGetterMethod(config, method, defaultName);
}
});
I didn't want to mess with some custom naming strategies, nor re-creating some accessors.
The less code, the happier I am.
This did the trick for us :
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonIgnoreProperties({"success", "deleted"}) // <- Prevents serialization duplicates
public class MyResponse {
private String id;
private #JsonProperty("isSuccess") boolean isSuccess; // <- Forces field name
private #JsonProperty("isDeleted") boolean isDeleted;
}
Building upon Utkarsh's answer..
Getter names minus get/is is used as the JSON name.
public class Example{
private String radcliffe;
public getHarryPotter(){
return radcliffe;
}
}
is stored as { "harryPotter" : "whateverYouGaveHere" }
For Deserialization, Jackson checks against both the setter and the field name.
For the Json String { "word1" : "example" }, both the below are valid.
public class Example{
private String word1;
public setword2( String pqr){
this.word1 = pqr;
}
}
public class Example2{
private String word2;
public setWord1(String pqr){
this.word2 = pqr ;
}
}
A more interesting question is which order Jackson considers for deserialization. If i try to deserialize { "word1" : "myName" } with
public class Example3{
private String word1;
private String word2;
public setWord1( String parameter){
this.word2 = parameter ;
}
}
I did not test the above case, but it would be interesting to see the values of word1 & word2 ...
Note: I used drastically different names to emphasize which fields are required to be same.
You can change primitive boolean to java.lang.Boolean (+ use #JsonPropery)
#JsonProperty("isA")
private Boolean isA = false;
public Boolean getA() {
return this.isA;
}
public void setA(Boolean a) {
this.isA = a;
}
Worked excellent for me.
If you are interested in handling 3rd party classes not under your control (like #edmundpie mentioned in a comment) then you add Mixin classes to your ObjectMapper where the property/field names should match the ones from your 3rd party class:
public class MyStack32270422 {
public static void main(String[] args) {
ObjectMapper om3rdParty = new ObjectMapper();
om3rdParty .addMixIn(My3rdPartyResponse.class, MixinMyResponse.class);
// add further mixins if required
String jsonString = om3rdParty.writeValueAsString(new My3rdPartyResponse());
System.out.println(jsonString);
}
}
class MixinMyResponse {
// add all jackson annotations here you want to be used when handling My3rdPartyResponse classes
#JsonProperty("isSuccess")
private boolean isSuccess;
}
class My3rdPartyResponse{
private boolean isSuccess = true;
// getter and setter here if desired
}
Basically you add all your Jackson annotations to your Mixin classes as if you would own the class. In my opinion quite a nice solution as you don't have to mess around with checking method names starting with "is.." and so on.
there is another method for this problem.
just define a new sub-class extends PropertyNamingStrategy and pass it to ObjectMapper instance.
here is a code snippet may be help more:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName) {
String input = defaultName;
if(method.getName().startsWith("is")){
input = method.getName();
}
//copy from LowerCaseWithUnderscoresStrategy
if (input == null) return input; // garbage in, garbage out
int length = input.length();
StringBuilder result = new StringBuilder(length * 2);
int resultLength = 0;
boolean wasPrevTranslated = false;
for (int i = 0; i < length; i++)
{
char c = input.charAt(i);
if (i > 0 || c != '_') // skip first starting underscore
{
if (Character.isUpperCase(c))
{
if (!wasPrevTranslated && resultLength > 0 && result.charAt(resultLength - 1) != '_')
{
result.append('_');
resultLength++;
}
c = Character.toLowerCase(c);
wasPrevTranslated = true;
}
else
{
wasPrevTranslated = false;
}
result.append(c);
resultLength++;
}
}
return resultLength > 0 ? result.toString() : input;
}
});
The accepted answer won't work for my case.
In my case, the class is not owned by me. The problematic class comes from 3rd party dependencies, so I can't just add #JsonProperty annotation in it.
To solve it, inspired by #burak answer above, I created a custom PropertyNamingStrategy as follow:
mapper.setPropertyNamingStrategy(new PropertyNamingStrategy() {
#Override
public String nameForSetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if (method.getParameterCount() == 1 &&
(method.getRawParameterType(0) == Boolean.class || method.getRawParameterType(0) == boolean.class) &&
method.getName().startsWith("set")) {
Class<?> containingClass = method.getDeclaringClass();
String potentialFieldName = "is" + method.getName().substring(3);
try {
containingClass.getDeclaredField(potentialFieldName);
return potentialFieldName;
} catch (NoSuchFieldException e) {
// do nothing and fall through
}
}
return super.nameForSetterMethod(config, method, defaultName);
}
#Override
public String nameForGetterMethod(MapperConfig<?> config, AnnotatedMethod method, String defaultName)
{
if(method.hasReturnType() && (method.getRawReturnType() == Boolean.class || method.getRawReturnType() == boolean.class)
&& method.getName().startsWith("is")) {
Class<?> containingClass = method.getDeclaringClass();
String potentialFieldName = method.getName();
try {
containingClass.getDeclaredField(potentialFieldName);
return potentialFieldName;
} catch (NoSuchFieldException e) {
// do nothing and fall through
}
}
return super.nameForGetterMethod(config, method, defaultName);
}
});
Basically what this does is, before serializing and deserializing, it checks in the target/source class which property name is present in the class, whether it is isEnabled or enabled property.
Based on that, the mapper will serialize and deserialize to the property name that is exist.

Apache Isis: #Property(editing = Editing.ENABLED) doesn't work for ViewModels

I added a property to ViewModel and marked it with Editing.ENABLED.
#DomainObject(
nature = Nature.VIEW_MODEL,
objectType = "homepage.HomePageViewModel"
)
public class HomePageViewModel {
#Setter #Getter
#Property(editing = Editing.ENABLED)
private String editableField;
}
But this field is not editable on UI:
But it works fine for SimpleObject:
Does it work correctly for ViewModel?
Maybe ViewModel shouldn't have any properties?
No, it isn't working correctly for view models... the framework is meant to support this.
The good news is that there is a workaround. If you annotate the class to use the (more flexible) JAXB-style of view model, then it all works as expected.
Here's an updated version of the class; look for annotations starting #Xml...:
#XmlRootElement(name = "compareCustomers")
#XmlType(
propOrder = {
"editableField"
}
)
#XmlAccessorType(XmlAccessType.FIELD)
public class HomePageViewModel {
#XmlElement(required = true)
#Setter #Getter
#Property(editing = Editing.ENABLED)
private String editableField;
public TranslatableString title() {
return TranslatableString.tr("{num} objects", "num", getObjects().size());
}
public List<SimpleObject> getObjects() {
return simpleObjectRepository.listAll();
}
#XmlTransient
#javax.inject.Inject
SimpleObjectRepository simpleObjectRepository;
}
For more on JAXB view models, see the user guide.
Meantime I've raised a JIRA ticket for the issue you've discovered,

Activator.CreateInstance and Ninject on asp.net mvc 4

I am trying to use reflection and ninject on the same project. Here is my code :
Type type = Type.GetType("MySolution.Project.Web.App_Code.DataClass");
MethodInfo theMethod = type.GetMethod("Events_ListAll");
object classInstance = Activator.CreateInstance(type, null);
And here is my class that contains that method:
public class DataClass
{
private IEventService eventService;
public DataClass(IEventService eventService)
{
this.eventService = eventService;
}
public String Events_ListAll()
{
List<Event> lstEvents = eventService.GetEvents().ToList<Event>();
return "";
}
}
I get an error saying that there is no constructor found. The solution to that would be to ad an empty default constructor, but that wont inject class I want. Is there any workaround to solve this?
You will need a concrete instance of IEventService to pass as parameter to ctor of DataClass, like this Activator.CreateInstance(type, instance);, so you got many approaches to do that, see 2 of :
1st - class has a concrete IEventService
That class where you doing the reflection has a concrete instance of IEventService and then you just pass as param to the Activator:
public class Foo
{
public Foo(IEventService eventService)
{
Type type = Type.GetType("MySolution.Project.Web.App_Code.DataClass");
MethodInfo theMethod = type.GetMethod("Events_ListAll");
object classInstance = Activator.CreateInstance(type, eventService);
}
}
2nd - Get IKernel implementation of Ninject
If you are using NinjectWebCommom you can just change the bootstrapper prop to public and get the kernel like this NinjectWebCommom.bootstrapper.Kernel.get<IEventService>()
Type type = Type.GetType("MySolution.Project.Web.App_Code.DataClass");
MethodInfo theMethod = type.GetMethod("Events_ListAll");
object classInstance = Activator.CreateInstance(type, Kernel.Get<IEventService>());

Serialization of Keys with Circular References using Jackson

I am trying to serialize a HashMap from Objects to Strings, but the specific Object has a reference to the current class leading to an infinite recursion, which doesn't seem to be solved with the usual JsonIdentifyInfo annotation. Here's an example:
public class CircularKey {
public void start() throws IOException {
ObjectMapper mapper = new ObjectMapper();
Cat cat = new Cat();
// Encode
String json = mapper.writeValueAsString(cat);
System.out.println(json);
// Decode
Cat cat2 = mapper.readValue(json, Cat.class);
System.out.println(mapper.writeValueAsString(cat2));
}
}
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
class Mouse {
int id;
#JsonProperty
Cat cat;
}
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
#JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "#class")
class Cat {
int id;
#JsonSerialize(keyUsing = MouseMapKeySerializer.class)
#JsonDeserialize(keyUsing = MouseMapKeyDeserializer.class)
#JsonProperty
HashMap<Mouse, String> status = new HashMap<Mouse, String>();
public Cat() {
Mouse m = new Mouse();
m.cat = this;
status.put(m, "mike");
}
}
Here's the serializer/deserializer for the key:
class MouseMapKeySerializer extends JsonSerializer<Mouse> {
static ObjectMapper mapper = new ObjectMapper();
#Override
public void serialize(Mouse value, JsonGenerator generator,
SerializerProvider provider) throws IOException,
JsonProcessingException {
String json = mapper.writeValueAsString(value);
generator.writeFieldName(json);
}
}
class MouseMapKeyDeserializer extends KeyDeserializer {
static ObjectMapper mapper = new ObjectMapper();
#Override
public Mouse deserializeKey(String c, DeserializationContext ctx)
throws IOException, JsonProcessingException {
return mapper.readValue(c, Mouse.class);
}
}
If I switch the map to HashMap (String,Object) it works but I cannot change the original mapping. Any ideas?
It looks like you found your answer at http://jackson-users.ning.com/forum/topics/serializing-hashmap-with-object-key-and-recursion. This doesn't seem to be possible because:
Complex keys are tricky, and it is not a use case I ever considered. Then again, there is nothing specifically preventing use of standard components; main concern was just the limitations than JSON has (must be String-value, JsonParser/JsonGenerator expose keys as different tokens).
There is no explicit support for either polymorphic types or object ids for Object keys. Standard serializers/deserializers are mostly for relatively simple types that can be easily and reliably converted to/from Strings; numbers, Dates, UUIDs.
So: unlike with value handlers, where modular design (with separation of TypeSerializer/JsonSerializer) makes sense, I think what you need to do is to have custom (de)serializers that handle all aspects. You should be able to use code from existing value (de)serializers, type (de)serializers, but not classes themselves.
Your use case does sound interesting, but for better or worse, it is pushing the envelope quite a bit. :-)

Jackson vector serialization exception

I have the following code with a simple class and a method for writing and then reading:
ObjectMapper mapper = new ObjectMapper();
try{
DataStore testOut = new DataStore();
DataStore.Checklist ch1 = testOut.addChecklist();
ch1.SetTitle("Checklist1");
String output = mapper.writeValueAsString(testOut);
JsonNode rootNode = mapper.readValue(output, JsonNode.class);
Map<String,Object> userData = mapper.readValue(output, Map.class);
}
public class DataStore {
public static class Checklist
{
public Checklist()
{
}
private String _title;
public String GetTitle()
{
return _title;
}
public void SetTitle(String title)
{
_title = title;
}
}
//Checklists
private Vector<Checklist> _checklists = new Vector<Checklist>();
public Checklist addChecklist()
{
Checklist ch = new Checklist();
ch.SetTitle("New Checklist");
_checklists.add(ch);
return ch;
}
public Vector<Checklist> getChecklists()
{
return _checklists;
}
public void setChecklists(Vector<Checklist> checklists)
{
_checklists = checklists;
}
}
The line:
String output = mapper.writeValueAsString(testOut);
causes an exception that has had me baffled for hours and about to abandon using this at all.
Any hints are appreciated.
Here is the exception:
No serializer found for class DataStore$Checklist and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: DataStore["checklists"]->java.util.Vector[0])
There are multiple ways to do it, but I will start with what you are doing wrong: your naming of getter and setter method is wrong -- in Java one uses "camel-case", so you should be using "getTitle". Because of this, properties are not found.
Besides renaming methods to use Java-style names, there are alternatives:
You can use annotation JsonProperty("title") for GetTitle(), so that property is recognized
If you don't want the wrapper object, you could alternatively just add #JsonValue for GetTitle(), in which case value used for the whole object would be return value of that method.
The answer seems to be: You can't do that with Json. I've seen comments in the Gson tutorial as well, that state that some serialization just doesn't work. I downloaded XStream and spat it out with XML in a few minutes of work and a lot less construction around what I really wanted to persist. In the process, I was able to delete a lot of code.