So, I have configuration
.defaultTyping(NON_FINAL)
which is writing correctly serialized json as:
"headers": [
"org.springframework.messaging.MessageHeaders",
{
Now, the problem is that MessageHeaders class is implementing Map, but also overrides put method so it throws an exception. In order to properly deserialize this I would need eaither to be able to serialize this as HashMap, so:
"headers": [
"java.util.HashMap",
{
or to be able to explicitly deserialize MessageHeaders as HashMap (since it is actually just Map.
Once more question is: how to serialize Object implementing Map as HashMap, or how to have Object implementing Map deserialized as HashMap.
Ok, i found how to do this with custom serializer (the trick was to tell serializer to use java.util.HashMap instead of the real class name):
public class MessageHeadersJsonSerializer extends JsonSerializer<MessageHeaders>{
#Override
public void serializeWithType(MessageHeaders value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
typeSer.writeTypePrefixForObject(value, gen, HashMap.class);
serialize(value, gen, serializers);
typeSer.writeTypeSuffixForObject(value, gen);
}
#Override
public void serialize(MessageHeaders value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
for(Map.Entry<String, Object> entry : value.entrySet()){
gen.writeFieldName(entry.getKey());
gen.writeObject(entry.getValue());
}
}
}
And then just registered it as a MixIn:
#JsonSerialize(using = MessageHeadersJsonSerializer.class)
public abstract class MessageHeadersMixIn {}
And on parent object I'm deserializing it as HashMap:
public abstract class GenericMessageMixIn<T> {
#JsonCreator
public GenericMessageMixIn(
#JsonProperty("payload") T payload,
#JsonProperty("headers") Map<String, Object> headers
){}
}
And finally all works OK!
Related
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();
}
I am able to serialise the string field using #JsonSerialize applied on POJO getmethod, and able to see the post serialize value changes in controller before binding to response, but once the controller method returns ResponseEntity.ok(postRes), the serialise value is not coming in the response.
The application is Spring Boot 2.1.1, JsonSerialize comes from Jackson.
Do I need to configure anything in Spring Boot for this?
Usage:
#JsonSerialize(using = JsonString.class)
public String getInvoices() {
return invoices;
}
Implementation:
public class JsonString extends JsonSerializer <String> {
#Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException,
JsonProcessingException {
String val = value.replace("/", "");
gen.writeString(val);
}
}
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
}
}
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();
}
}
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.