4

There are many questions concerning conversion from ObjectId to String with jackson. All answers suggest either creating own JsonSerializer<ObjectId> or annotating the ObjectId field with @JsonSerialize(using = ToStringSerializer.class).

However, I have a map that sometimes contains ObjectIds, i.e.:

class Whatever {
  private Map<String, Object> parameters = new HashMap<>();
  Whatever() {
    parameters.put("tom", "Cat");
    parameters.put("jerry", new ObjectId());
  }
}

I want jackson to convert it to:

{
  "parameters": {
    "tom": "cat",
    "jerry": "57076a6ed1c5d61930a238c5"
  }
}

But I get:

{
  "parameters": {
    "tom": "cat",
    "jerry": {
      "date": 1460103790000,
      "machineIdentifier": 13747670,
      "processIdentifier": 6448,
      "counter": 10631365,
      "time": 1460103790000,
      "timestamp": 1460103790,
      "timeSecond": 1460103790
    }
  }
}

I have registered the conversion (in Spring) with

public class WebappConfig extends WebMvcConfigurerAdapter {
  @Override
  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder
        .serializerByType(ObjectId.class, new ToStringSerializer());
    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
    converters.add(converter);
  }
}

And the first-level ObjectIds are converted correctly. How to make jackson convert also the nested ones? Do I have to write custom converter for this map?

Keep in mind that this Map can be nested multiple times (i.e. contain another maps). I just want to convert ObjectId to String whenever jackson sees it.

Community
  • 1
  • 1
fracz
  • 20,536
  • 18
  • 103
  • 149

2 Answers2

6

I suppose that you are taking about org.bson.types.ObjectId from org.springframework.boot:spring-boot-starter-data-mongodb. Your code works perfectly fine for me. 1 thing i can see is that you don't show @Configuration annotation above WebappConfig.

Here is my demo project, can you test it on yours setup?

Application.java

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.bson.types.ObjectId;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Configuration
    public static class WebappConfig extends WebMvcConfigurerAdapter {
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
            builder
                    .serializerByType(ObjectId.class, new ToStringSerializer());
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build());
            converters.add(converter);
        }
    }

    @RestController
    public static class MyRestController {

        @ResponseBody
        @RequestMapping("/")
        public Whatever method() {
            return new Whatever();
        }
    }

    public static class Whatever {
        private Map<String, Object> parameters = new HashMap<>();

        public Whatever() {
            parameters.put("tom", "Cat");
            parameters.put("jerry", new ObjectId());
        }

        public Map<String, Object> getParameters() {
            return parameters;
        }

        public void setParameters(Map<String, Object> parameters) {
            this.parameters = parameters;
        }
    }
}

Responce from 127.0.0.1:8080

{
  "parameters": {
    "tom": "Cat",
    "jerry": "5709df1cf0d9550b4de619d2"
  }
}

Gradle:

dependencies {
    compile("org.springframework.boot:spring-boot-starter-data-mongodb")
    compile("org.springframework.boot:spring-boot-starter-web")
}
varren
  • 14,551
  • 2
  • 41
  • 72
  • I have created a [minimal *not* working example](https://github.com/fracz/stackoverflow-problems/tree/jackson-nested-objectid-tostring) and it is mapped correctly for me, too. It seems that I have to investigate the problem more carefully. Thank you for checking this, I will post an update if I find something. – fracz Apr 10 '16 at 09:16
  • I can confirm now that it was a mistake in another place of the application. The `Map` was converted by another instance of the object mapper from jackson instead of the one used in Spring context. It did not have th `ObjectId` configuration applied so it could not work. Thank you for forcing be to create a minimal example and sorry for my mistake! – fracz Apr 10 '16 at 13:12
  • 1
    I tried running Application.java as posted above with no luck. Using the @JsonSerialize annotation as described here worked though: http://imalhasaranga.com/2014/07/20/238/ – jsaven Oct 05 '18 at 10:19
0

thanks varren's answer, it works fine in springMvc's older version. but since 5.0, WebMvcConfigurerAdapter was deprecated. solution:

  1. may not work solution: we can implements WebMvcConfigurer directly for mvc config. but some config may not work, because WebMvcConfigurationSupport's priority is higher.
  2. suggest solution: we can extends WebMvcConfigurationSupport directly. imply configureMessageConverters method can add all kinds of custom HttpMessageConverters we need, and it can works fine before default converters.

spring framework is a amazing framework, I need to look it deeper after I got time.(●'◡'●)

wangyk
  • 53
  • 7