FasterXML / FasterXML/jackson-databind

Jackson3 finds constructors which Jackson2 did not, changing property serialization order

Open
#6,032 14 comments 0 reactions 0 assignees View on GitHub
3.x
Dominant language
Java
Stars
3.7k
Forks
1.5k
Avg merge
3d 6h
Merged PRs (30d)
28

Description

### Search before asking

- [x] I searched in the [issues](https://github.com/FasterXML/jackson-databind/issues) and found nothing similar.

### Describe the bug

In Jackson3 **only**, constructors which are not annotated with ConstructorProperties are detected and cause their fields to be sorted to the top, according to the `SORT_CREATOR_PROPERTIES_FIRST` flag.

However, there does not seem to be any way to disable this without disabling the flag (which would break other things for me)

I've tried adding all of these extra configurations and Jackson3 still sorts the fields in the unannotated constructor to the top.
```java
.constructorDetector(ConstructorDetector.EXPLICIT_ONLY)
.disable(MapperFeature.INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES)
.changeDefaultVisibility(vc -> vc.withCreatorVisibility(JsonAutoDetect.Visibility.NONE))
```

I think in most cases, this would not matter. But in my case, I am serializing java objects to json to use as Keys in redis, so any change whatsoever in the output will invalidate my entire cache.

I have a workaround: Adding JsonIgnore to every constructor which Jackson2 was not able to find. But I'm hoping there is, or could be, a way to configure my way out of this.

### Version Information

3.2.0

### Reproduction

jackson2_WithAnnotation -> pass
jackson3_WithAnnotation -> pass
jackson2_WithoutAnnotation -> pass
jackson3_WithoutAnnotation -> fail

```java
import static org.assertj.core.api.BDDAssertions.then;
import java.beans.ConstructorProperties;
import org.junit.jupiter.api.Test;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.MapperFeature;
import tools.jackson.databind.json.JsonMapper;

class RedisMapperReproTest {

static final class WithoutAnnotation {
private final String aa = "a";
private final String bb;

public WithoutAnnotation(String bb) {
this.bb = bb;
}

public String getAa() {
return this.aa;
}

public String getBb() {
return this.bb;
}
}

static final class WithAnnotation {
private final String aa = "a";
private final String bb;

@ConstructorProperties({"bb"})
public WithAnnotation(String bb) {
this.bb = bb;
}

public String getAa() {
return this.aa;
}

public String getBb() {
return this.bb;
}
}

private static final WithoutAnnotation WITHOUT_ANNOTATION = new WithoutAnnotation("b");
private static final WithAnnotation WITH_ANNOTATION = new WithAnnotation("b");
private static final tools.jackson.databind.json.JsonMapper jackson3Mapper = JsonMapper.builder()
.enable(MapperFeature.SORT_CREATOR_PROPERTIES_FIRST)
.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY)
.build();
private static final com.fasterxml.jackson.databind.json.JsonMapper jackson2Mapper =
new com.fasterxml.jackson.databind.json.JsonMapper();

private final String expectedWithAnnotation = /* language=json */ """
{"bb":"b","aa":"a"}""";

@Test
void jackson2_WithAnnotation() throws Exception {
then(jackson2Mapper.writeValueAsString(WITH_ANNOTATION))
.isEqualTo(expectedWithAnnotation);
}

@Test
void jackson3_WithAnnotation() {
then(jackson3Mapper.writeValueAsString(WITH_ANNOTATION))
.isEqualTo(expectedWithAnnotation);
}

private final String expectedWithoutAnnotation = /* language=json */ """
{"aa":"a","bb":"b"}""";

@Test
void jackson2_WithoutAnnotation() throws Exception {
then(jackson2Mapper.writeValueAsString(WITHOUT_ANNOTATION))
.isEqualTo(expectedWithoutAnnotation);
}

@Test
void jackson3_WithoutAnnotation() {
then(jackson3Mapper.writeValueAsString(WITHOUT_ANNOTATION))
.isEqualTo(expectedWithoutAnnotation);
}
}

```

### Expected behavior

_No response_

### Additional context

_No response_

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.