Jackson fails to serialize Joda DateTimeFormatter - java

I am trying to return a JSON in my Spring MVC 3 application, but its failing for Joda DateTimeFormatter
com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.joda.time.format.DateTimeFormat$StyleFormatter and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.HashMap["personDay"]->mypackage.PersonDay["dateTimeFormatter"]->org.joda.time.format.DateTimeFormatter["parser"])
It looks like i might need a custom serializer for this, but i am not sure where to begin.

You can take a look here for more details and options.
Basically, you need to create a Serializer, something like:
public class ItemSerializer extends StdSerializer<Item> {
public ItemSerializer() {
this(null);
}
public ItemSerializer(Class<Item> t) {
super(t);
}
#Override
public void serialize(Item value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", value.id);
jgen.writeStringField("itemName", value.itemName);
jgen.writeNumberField("owner", value.owner.id);
jgen.writeEndObject();
}
}
Then you can annotate your class with: #JsonSerialize, something like:
#JsonSerialize(using = ItemSerializer.class)
public class Item {
public int id;
public String itemName;
public User owner;
}

Related

Serialize objects to a map with the object class as the key?

I'm writing a application using Spring boot and jackson for JSON parsing. I need to handle another service which produces JSON like this:
{
"task-id": 5081,
"task-created-on": {
"java.util.Date": 1631022026000
}
}
Notably, certain fields like the date field here are serialized into a map with a single key-value pair, where the key is a java classname and the value is the actual value of the field.
I've been going through the jackson documentation and haven't found anything about this format. Is there a way to configure jackson to produce and parse fields in this format?
At a minimum, I need to handle dates formatted this way. But I believe the service also uses this format for other objects, where the map key will be the name of some arbitrary java class and the value will be a map of its own. So I'd be interested in a solution that handles more than just dates if possible.
It can be easily done with custom serializer in Jackson by following steps.
First, create objects for serialization as follows:
class MyDateObject {
private Date date;
//general getter/setter
}
class Task {
#JsonProperty("task-id")
private int taskId;
#JsonProperty("task-created-on")
private MyDateObject taskCreatedOn;
//general getters/setters
}
Second, define your custom serializer: (Please note that I used myDateObject.getDate().getClass().getName() to get the class name of date field.)
class DateSerializer extends StdSerializer<MyDateObject> {
public DateSerializer() {
this(null);
}
protected DateSerializer(Class<MyDateObject> t) {
super(t);
}
#Override
public void serialize(MyDateObject myDateObject, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeNumberField(myDateObject.getDate().getClass().getName(), myDateObject.getDate().getTime());
jsonGenerator.writeEndObject();
}
}
Finally, register the serializer with ObjectMapper for the MyDateObject class and perform the serialization:
MyDateObject myDateObject = new MyDateObject();
myDateObject.setDate(new Date());
Task task = new Task();
task.setTaskId(5081);
task.setTaskCreatedOn(myDateObject);
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(MyDateObject.class, new DateSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(simpleModule);
System.out.println(objectMapper.writeValueAsString(task));
The expected output is:
{"task-id":5081,"task-created-on":{"java.util.Date":1633402076254}}
Please refer to Jackson – Custom Serializer for more information.
It is possible to solve the issue with the use of a custom JsonSerializer and applying the JsonSerialize over the fields in the pojo you are interested like below :
public class Task {
#JsonProperty("task-id")
private int taskId;
#JsonProperty("task-created-on")
#JsonSerialize(using = ObjectSerializer.class)
Date taskCreatedOn;
}
The custom serializer will use the JsonGenerator.html#writeObjectField to serialize a generic object (Date or other java class) as propertyname : {"classname" : value} :
public class ObjectSerializer extends JsonSerializer<Object> {
#Override
public void serialize(Object t, JsonGenerator jg, SerializerProvider sp) throws IOException {
jg.writeStartObject();
jg.writeObjectField(t.getClass().getName(), t);
jg.writeEndObject();
}
}

Generic POJO to JSON using jackson with dynamic json key as per Generic class

I have a POJO like:
class Wrapper,T> {
private int count;
private T data;
// getters setters
}
While converting it to JSON using Jackson, json is something like:
{
count:1,
**data**:{}
}
I need the data key to be changed as per class name T or some other value related to class name, how can I achieve this. Please suggest.
Thankyou.
Using a custom serializer, you can do anything you want since you have complete control over the serialization process. See for example https://www.baeldung.com/jackson-custom-serialization.
Your serialize method would look something like this:
#Override
public void serialize(
Wrapper<?> value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String derivedName = value.getClass().getSimpleName() + "Data"; // just an example
jgen.writeStartObject();
jgen.writeNumberField("count", value.getCount());
jgen.writeObjectField(derivedName, value.getData());
jgen.writeEndObject();
}

how to get property or field name in a custom json serializer

I have a custom JsonSerializer for a field (simplified code):
#JsonSerialize(using=Text1Serializer.class)
#JsonProperty("text1") // I need this inside the custom serializer
#Override
public String getTextOne() {
return "foo";
}
// ...
public static class Text1Serializerextends JsonSerializer<String> {
#Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
// how to get "text1" here?
provider.defaultSerializeValue(value, jgen);
}
}
Since I need to serialize about ten other fields with a similar logic, that just depends on the field name, it would help me very much if I could get the property name inside the custom serializer - instead of writing ten identical serializers.
I've seen that inside the serialize() method I can get the whole object with JsonGenerator.getCurrentValue() (see this answer), but I didnt' find a way to get the field name.
I'm using Jackson 2.6
You can get field name in a custom json serializer like this:
#JsonComponent
public class Text1Serializerextends extends JsonSerializer<String> {
#Override
public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
// will output "text1"
System.out.println(jgen.getOutputContext().getCurrentName());
provider.defaultSerializeValue(value, jgen);
}
}
If you implement ContextualSerializer, this will be used to produce a "contextual" version of your serializer, i.e. one that is configured using the BeanProperty:
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property)
throws JsonMappingException;
This should return a new instance that is customised for the given property: it doesn't have to be the same class as the non-customised serializer (although the standard Jackson implementations all seem to work that way).
You can get the current property name by calling jgen.getOutputContext().getCurrentName()
You can achieve this through Customized SerzializerProvider
public class EmptyContentSerializerProvider extends DefaultSerializerProvider {
#Override
public JsonSerializer<Object> findNullValueSerializer(BeanProperty property) throws JsonMappingException {
property.getName(); //this can extract the filed name
}
}

Getting dynamic property names with jackson

I need to create json where the objects are all structured similarly, but can contain different object names, i.e.:
"obj1":{
"field1":1,
"field2":2
}
"obj2":{
"field1":4,
"field2":5
}
"obj3":{
"field1":7,
"field2":8
}
How can I use jackson to create dynanic field names? this would be done during run time depending on input taken
You could possibly refer to this answer: Jackson dynamic property names.
Basically you can use a custom JsonSerializer.
#JsonProperty("p")
#JsonSerialize(using = CustomSerializer.class)
private Object data;
// ...
public class CustomSerializer extends JsonSerializer<Object> {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeObjectField(value.getClass().getName(), value);
jgen.writeEndObject();
}
}

JSON Jackson - exception when serializing a polymorphic class with custom serializer

I'm currently migrating some code from Jackson 1.x to Jackson 2.5 json mapper and came a long a problem that wasn't there in 1.x.
This is the setup (see code below):
interface IPet
class Dog implements IPet
IPet is annotated with #JsonTypeInfo and #JsonSubTypes
class Human has a property of type IPet that is annotated with #JsonSerialize(using=CustomPetSerializer.class)
The problem:
If I serialize an instance of Dog it works as expected (also the type info is added to the json string by Jackson).
However when I serialize an instance of the Human class an exception is thrown saying:
com.fasterxml.jackson.databind.JsonMappingException: Type id handling
not implemented for type com.pet.Dog (through reference chain:
com.Human["pet"])
The serialize(...) method of the CustomPetSerializer class is not invoked (tested using a breakpoint).
The code:
IPet implementation:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
#JsonSubTypes({
#JsonSubTypes.Type(value=Dog.class, name="dog")
//,#JsonSubTypes.Type(value=Cat.class, name="cat")
//more subtypes here...
})
public interface IPet
{
public Long getId();
public String getPetMakes();
}
Dog implementation:
public class Dog implements IPet
{
#Override
public String getPetMakes()
{
return "Wuff!";
}
#Override
public Long getId()
{
return 777L;
}
}
Human who owns a dog:
public class Human
{
private IPet pet = new Dog();
#JsonSerialize(using=CustomPetSerializer.class)
public IPet getPet()
{
return pet;
}
}
CustomPetSerializer implementation:
public class CustomPetSerializer extends JsonSerializer<IPet>
{
#Override
public void serialize(IPet value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException
{
if(value != null && value.getId() != null)
{
Map<String,Object> style = new HashMap<String,Object>();
style.put("age", "7");
gen.writeObject(style);
}
}
}
JUnit test method:
#Test
public void testPet() throws JsonProcessingException
{
ObjectMapper mapper = new ObjectMapper();
Human human = new Human();
//works as expcected
String json = mapper.writeValueAsString(human.getPet());
Assert.assertNotNull(json);
Assert.assertTrue(json.equals("{\"type\":\"dog\",\"id\":777,\"petMakes\":\"Wuff!\"}"));
//throws exception: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])
json = mapper.writeValueAsString(human); //exception is thrown here
Assert.assertNotNull(json);
Assert.assertTrue(json.contains("\"age\":\"7\""));
}
You'll need to additionally override serializeWithType within you CustomPetSerializer because IPet is polymorphic. That's also the reason why serialize is not called. Check this related SO question that explains in detail when serializeWithType is called. For instance, your serializeWithType implementation might look something like this:
#Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
typeSer.writeTypePrefixForObject(value, gen);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffixForObject(value, gen);
}
which will print {"pet":{"type":"dog":{"age":"7"}}} for your Human instance.
Since Jackson 2.9 writeTypePrefixForObject() and writeTypeSuffixForObject() have been deprecated (I'm unclear why). It seems under the new approach it would now be:
#Override
public void serializeWithType(IPet value, JsonGenerator gen,
SerializerProvider provider, TypeSerializer typeSer)
throws IOException, JsonProcessingException {
WritableTypeId typeId = typeSer.typeId(value, START_OBJECT);
typeSer.writeTypePrefix(gen, typeId);
serialize(value, gen, provider); // call your customized serialize method
typeSer.writeTypeSuffix(gen, typeId);
}
So an extra line now, so not sure why it's a step forward, perhaps it's more efficient reusing the typeId object.
Source: Jackson's ObjectNode class currently in master. Not the best source but couldn't see any upgrade docs explaining what to do.

Categories

Resources