0

Related code for springboot jackson settings.

@Configuration
class JacksonConfig {

    @Bean
    fun jackson2ObjectMapperBuilderCustomizer(): Jackson2ObjectMapperBuilderCustomizer =
        Jackson2ObjectMapperBuilderCustomizer { builder ->
            builder.serializerByType(Long::class.java, ToStringSerializer.instance)
        }

}

But this custom serializer only works partially.

@RestController
class Controller {

    @GetMapping("/1")
    fun hello(): Person{
        return Person(116391232976064512L)
    }

    @GetMapping("/2")
    fun hello2(): Student{
        return Student(116391232976064512L)
    }

    @GetMapping("/3")
    fun hello3(): Product{
        return Product(116391232976064512L)
    }

}

data class Person(
    val id: Long
)

data class Student(
    val id: Long? = null
)

I created java object again to test.

public class Product {

    private Long id;

    public Product(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

only the /1 api successfully converts Long to String type, and the other two api do not.

Am I setting the global serializer wrong?

output:

// get http://localhost:8080/1
{"id":"116391232976064512"}

// get http://localhost:8080/2
{"id":116391232976064512}

// get http://localhost:8080/3
{"id":116391232976064512}

If you look closely, you will see that only api/1 returns String type as expected.

I am using springboot built-in jackson support:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.6</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

<dependency>
      <groupId>com.fasterxml.jackson.module</groupId>
      <artifactId>jackson-module-kotlin</artifactId>
    </dependency>

result

With the help of @M.Deinum I found the problem, there was something wrong with the way kotlin was set up. reference

The correct settings are as follows:

@Configuration
class JacksonConfig {

    @Bean
    fun jackson2ObjectMapperBuilderCustomizer(): Jackson2ObjectMapperBuilderCustomizer =
        Jackson2ObjectMapperBuilderCustomizer { builder ->
            builder.serializerByType(Long::class.java, ToStringSerializer.instance)
            builder.serializerByType(Long::class.javaObjectType, ToStringSerializer.instance)
        }

}
SageJustus
  • 631
  • 3
  • 9
  • What is the output you get vs. what you expect? I don't see anything, obviously, wrong with this. – M. Deinum May 16 '23 at 06:01
  • @M.Deinum only the `/1` api successfully converts `Long` to `String` type, and the other two api returned type is `Long`. – SageJustus May 16 '23 at 08:28
  • I'm sorry stating it does not work isn't really helpful, re-iterating what you already stated in the question isn't helpful either else I wouldn't ask for clarification. What do you see that happens vs. what do you expect to happen. Show some output! I also wonder how you are calling this. – M. Deinum May 16 '23 at 08:30
  • @M.Deinum I have added the output section in the problem description. – SageJustus May 16 '23 at 08:39
  • I suspect the difference is due to `val Long` becoming a `long` whereas the others are an object `Long`. YOu can test this by changing the `Long` in `Product` to `long` and I expect it then to become a `String`. – M. Deinum May 16 '23 at 08:51
  • @M.Deinum Yes, I tested it just now and you were right in your suspicion. But this solution is not perfect, there is no `long` type in Kotlin, and the wrapper class `Long` is often used instead of `long` in Java. – SageJustus May 16 '23 at 09:07
  • If you want all numbers as strings you can set the `JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS` to `true` that should write all numbers as string (instead of what you have now). – M. Deinum May 16 '23 at 09:21
  • `Long::class.java` is this a typo ? – PeterMmm May 16 '23 at 09:26
  • @M.Deinum I found out that this property `JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS` is deprecated in [answer](https://stackoverflow.com/a/62393349/14887783) – SageJustus May 16 '23 at 09:29
  • @PeterMmm It's not a typo, it's kotlin code. – SageJustus May 16 '23 at 09:30
  • It isn't deprecated, the previous one in `JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS` was deprecated. The one in `JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS` is the replacement for this. – M. Deinum May 16 '23 at 09:32
  • @M.Deinum `JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS` works, you can write this as an answer. If you can, can you write why the custom serializer is not working? Then actually I only need to convert `Long` type, because `javascript` loses precision when receiving `Java.lang.Long`. It's a compromise, but not the best. Sorry, maybe I'm asking a bit too much. – SageJustus May 16 '23 at 09:49

1 Answers1

1

If you want to write numbers as string in JSON then configure Jackson using the JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS and set the property to true.

@Bean
fun jackson2ObjectMapperBuilderCustomizer(): Jackson2ObjectMapperBuilderCustomizer =
  Jackson2ObjectMapperBuilderCustomizer { builder ->
   builder.featuresToEnable(JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS)
}

Or specify it in your application.properties, then you don't need the customizer.

spring.jackson.generator.WRITE_NUMBERS_AS_STRINGS=true

The issue with your code is that the val Long and val Long? from Kotlin are treated differently. The first will become a long whereas the second will become a Long (as it can be null). The serializer you registered applies to the first but not the second, for which I suspect the standard Jackson serializer will now kick in.

M. Deinum
  • 115,695
  • 22
  • 220
  • 224
  • but `Product` is a Java class and there is no conversion ( I excluded dependency `jackson-module-kotlin` ). – SageJustus May 16 '23 at 10:06
  • by the way, you may need to use `JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS.mappedFeature()` instead of `JsonWriteFeature.WRITE_NUMBERS_AS_STRINGS` – SageJustus May 16 '23 at 10:08
  • In `Product` it already is a `java.lang.Long` instead of a primitive `long`. – M. Deinum May 16 '23 at 10:15