Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,297 @@
package com.fasterxml.jackson.failing;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import java.util.List;

// [databind#3964] MismatchedInputException, Bean not yet resolved
public class JsonIdentityInfoAndBackReferences3964Test extends BaseMapTest
{
/**
* Fails : Original test
*/
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Tree.class
)
public static class Tree {
private final int id;
private List<Fruit> fruits;

@JsonCreator
public Tree(@JsonProperty("id") int id, @JsonProperty("fruits") List<Fruit> fruits) {
this.id = id;
this.fruits = fruits;
}

public int getId() {
return id;
}

public List<Fruit> getFruits() {
return fruits;
}

public void setFruits(List<Fruit> fruits) {
this.fruits = fruits;
}
}

@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Fruit.class
)
public static class Fruit {
private final int id;
private List<Calories> calories;
@JsonBackReference("id")
private Tree tree;

@JsonCreator
public Fruit(@JsonProperty("id") int id, @JsonProperty("calories") List<Calories> calories) {
this.id = id;
this.calories = calories;
}

public int getId() {
return id;
}

public Tree getTree() {
return tree;
}

public void setTree(Tree tree) {
this.tree = tree;
}

public List<Calories> getCalories() {
return calories;
}

public void setCalories(List<Calories> calories) {
this.calories = calories;
}
}

@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Calories.class
)
public static class Calories {
private final int id;
private Fruit fruit;

@JsonCreator
public Calories(@JsonProperty("id") int id) {
this.id = id;
}

public int getId() {
return id;
}

public Fruit getFruit() {
return fruit;
}

public void setFruit(Fruit fruit) {
this.fruit = fruit;
}
}

/**
* Fails : Lean version that fails and Without getters and setters
*/
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Animal.class
)
public static class Animal {
public final int id;
public List<Cat> cats;

@JsonCreator
public Animal(@JsonProperty("id") int id, @JsonProperty("cats") List<Cat> cats) {
this.id = id;
this.cats = cats;
}
}

@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Cat.class
)
public static class Cat {
public int id;
public List<Food> foods;
@JsonBackReference("id")
public Animal animal;

@JsonCreator
public Cat(@JsonProperty("id") int id, @JsonProperty("foods") List<Food> foods) {
this.id = id;
this.foods = foods;
}
}

@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Food.class
)
public static class Food {
public int id;
public Cat cat;

@JsonCreator
public Food(@JsonProperty("id") int id) {
this.id = id;
}
}

/**
* Passes : Testing lean without getters and setters
* and also without {@link JsonCreator}.
*/
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, below is test case.

@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Fish.class
)
public static class Fish {
public int id;
public List<Squid> squids;
}

@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Squid.class
)
public static class Squid {
public int id;
public List<Shrimp> shrimps;
@JsonBackReference("id")
public Fish fish;
}

@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id",
scope = Shrimp.class
)
public static class Shrimp {
public int id;
public Squid squid;
}

/*
/**********************************************************************
/* Test methods
/**********************************************************************
*/

final ObjectMapper MAPPER = jsonMapperBuilder()
.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build();

/**
* Fails : Original test
*/
public void testOriginal() throws JsonProcessingException {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you change this to just throw Exception (since 3.0 has different base exception)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about this repeated forgetting to change to Exception repeatedly 🥲

I will find a mechanim to prevent this from happening 👍🏻👍🏻

String json = "{" +
" \"id\": 1,\n" +
" \"fruits\": [\n" +
" {\n" +
" \"id\": 2,\n" +
" \"tree\": 1,\n" +
" \"calories\": [\n" +
" {\n" +
" \"id\": 3,\n" +
" \"fruit\": 2\n" +
" }\n" +
" ]\n" +
" }\n" +
" ]\n" +
" }";

try {
Tree tree = MAPPER.readValue(json, Tree.class);
// should reach here and pass... but throws Exception and fails
assertEquals(tree, tree.fruits.get(0).tree);
} catch (MismatchedInputException e) {
verifyException(e, "Cannot resolve ObjectId forward reference using property 'animal'");
verifyException(e, "Bean not yet resolved");
fail("Should not reach");
}
}

/**
* Fails : Lean version that fails and Without getters and setters
*/
public void testLeanWithoutGetterAndSetters() throws JsonProcessingException {
String json = a2q("{" +
" 'id': 1," +
" 'cats': [" +
" {" +
" 'id': 2," +
" 'animal': 1," + // reference here
" 'foods': [" +
" {" +
" 'id': 3," +
" 'cat': 2" +
" }" +
" ]" +
" }" +
" ]" +
" }");

try {
Animal animal = MAPPER.readValue(json, Animal.class);
// should reach here and pass... but throws Exception and fails
assertEquals(animal, animal.cats.get(0).animal);
} catch (MismatchedInputException e) {
verifyException(e, "Cannot resolve ObjectId forward reference using property 'animal'");
verifyException(e, "Bean not yet resolved");
fail("Should not reach");
}
}

/**
* Passes : Testing lean without getters and setters
* and also without {@link JsonCreator}.
*/
public void testLeanWithoutGetterAndSettersAndCreator() throws Exception {
String json = a2q("{" +
" 'id': 1," +
" 'squids': [" +
" {" +
" 'id': 2," +
" 'fish': 1," + // back reference
" 'shrimps': [" +
" {" +
" 'id': 3," +
" 'squid': 2" +
" }" +
" ]" +
" }" +
" ]" +
" }");

Fish fish = MAPPER.readValue(json, Fish.class);
assertEquals(fish, fish.squids.get(0).fish);
}
}