When updating from spring boot 3.2.8 to 3.2.11 an error started to show when I try to persist an object. I tried the 3.2.10 version and the error disapeared. It seems like a bug, since my code stopped to work in 3.2.11.

The error is related to a composite entity. A simplified version of my scenario is as follows:

Spring Boot:

@Entity
public class Estado {
    @Id
    @GeneratedValue( strategy = GenerationType.IDENTITY )
    private Long id;
    private String nome;
    private String sigla;
}

@Entity
public class Cidade {
    @Id
    @GeneratedValue( strategy = GenerationType.IDENTITY )
    private Long id;
    private String name;
    private Estado estado;
}

@RepositoryRestResource( collectionResourceRel = "cidades", path = "cidades", excerptProjection = CidadeInfo.class )
public interface CidadeRepository extends JpaRepository<Cidade , Long> {
}

When trying to persist a Cidade (city) which have an Estado (state/province), the following error appeared:

POST method:

JSON parse error: Cannot construct instance of br.com.studiocore.companion.entidades.Estado (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('/api/estados/1')

PATCH method:

Could not read payload

My JavaScript code:

const obj = new Cidade( 
    hdId.value === "" ? null : hdId.value, 
    txtNome.value,
    contextPath + "api/estados/" + selEstado.value
);

const id = hdId.value ? hdId.value : null;

fetch( contextPath + "api/cidades" + ( id ? `/${id}` : "" ), {
    method: id ? "PATCH" : "POST",
    headers: {
        "Authorization": "Bearer " + localStorage.getItem( "token" ),
        "Accept": "application/json",
        "Content-Type": "application/json"
    },
    body: JSON.stringify( obj )
}).then( response => {
    modalAguarde.hide();
    if ( response.ok ) {
        return response.json();
    }
    response.text().then( text => {
        let error = JSON.parse( text );
        let mensagem = "<p>Ocorreu um erro ao tentar salvar uma Cidade!</p>";
        mensagem += `<p>${error.message}</p>`;
        Utils.abrirModalMensagem( "Erro", mensagem );
    });
    throw new Error( "" );
}).then( data => {
    resetarFormulario();
    carregar( paginaAtual.pagina );
}).catch( error => {
    Utils.mostrarErro( error );
});

Comment From: bclozel

Please share a minimal sample application that reproduces the problem.

Because it involves JSON deserialisation, maybe we don't need to include JPA nor any JavaScript (a simple curl command should work).

Comment From: davidbuzatto

Here is the sample app: https://github.com/davidbuzatto/isthisabug

3.2.8 and 3.2.10:

POST: curl -d "{\"bar\":\"bars/1\"}" -H "Content-Type: application/json" -X POST http://localhost:8080/foos

Response OK:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/foos/2"
    },
    "foo" : {
      "href" : "http://localhost:8080/foos/2"
    },
    "bar" : {
      "href" : "http://localhost:8080/foos/2/bar"
    }
  }
}

PATCH: curl -d "{\"bar\":\"bars/1\"}" -H "Content-Type: application/json" -X PATCH http://localhost:8080/foos/1

Response OK:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/foos/1"
    },
    "foo" : {
      "href" : "http://localhost:8080/foos/1"
    },
    "bar" : {
      "href" : "http://localhost:8080/foos/1/bar"
    }
  }
}

3.2.11:

POST: curl -d "{\"bar\":\"bars/1\"}" -H "Content-Type: application/json" -X POST http://localhost:8080/foos

Error: {"cause":{"cause":null,"message":"Cannot construct instance ofcom.example.demo.Bar(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('bars/1')\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 1, column: 8] (through reference chain: com.example.demo.Foo[\"bar\"])"},"message":"JSON parse error: Cannot construct instance ofcom.example.demo.Bar(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('bars/1')"}

PATCH: curl -d "{\"bar\":\"bars/1\"}" -H "Content-Type: application/json" -X PATCH http://localhost:8080/foos/1

Error:

{"cause":{"cause":null,"message":"Cannot construct instance ofcom.example.demo.Bar(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('bars/1')\n at [Source: UNKNOWN; byte offset: #UNKNOWN] (through reference chain: com.example.demo.Foo[\"bar\"])"},"message":"Could not read payload"}

Comment From: bclozel

Thanks for the sample, I think this is a regression in Spring Data. Using Spring Boot 3.2.11 but downgrading Spring Data works:

    <properties>
        <spring-data-bom.version>2023.1.10</spring-data-bom.version>
    </properties>

I think this has been reported already in https://github.com/spring-projects/spring-data-rest/issues/2425 so I'm closing this issue as a duplicate.