title: Spring boot data rest, Jackson: How to use URI instead of id for related entity during Jackson serialization
body: <p>I have a one-to-many relationship b/w Hotel and Room entities. The data flow is as follows:</p>
  
  <ol>
  <li>client code creates Hotel object w/o any rooms </li>
  <li>client does HTTP POST (to create &amp; persist Hotel entity) </li>
  <li>client creates Room object, adds it to Hotel </li>
  <li>client does HTTP POST (to create ROOM and update the
  Hotel object)</li>
  </ol>
  
  <p>I have simulated above flow in the junit test below and running into exceptions shown below. </p>
  
  <pre><code>Method: POST
  URI: http://localhost:49515/api/hotels
  Request Body: {"id":100,"name":"hotel1"
  
  Method: POST
  URI: http://localhost:49515/api/rooms
  Request Body: {"id":200,"name":"room1","hotel":100}
  
  o.s.d.r.w.RepositoryRestExceptionHandler : Could not read document: Failed to convert from type [java.net.URI] to type [com.example.entities.Hotel] for value '100'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 100. Is it local or remote? Only local URIs are resolvable. (through reference chain: com.example.entities.Room["hotel"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: 
  </code></pre>
  
  <p>Problem seems to be that when posting for the Room object creation, the reference to Hotel is sent as a pure ID field (during serialization) and Jackson is not able to resolve that reference during deserialization.  How do I send the URI of the Hotel instead of its ID?</p>
  
  <pre><code>{"id":200,"name":"room1", "hotel": "http://localhost:49515/api/hotels/100"}
  </code></pre>
  
  <p>instead of</p>
  
  <pre><code>    {"id":200,"name":"room1", "hotel":100}
  </code></pre>
  
  <p>Is there an alternative way to solve my data flow?</p>
  
  <p>Sample code below:</p>
  
  <pre><code>    @Entity
      @Table
      @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
      public class Hotel implements Serializable {
          private static final long serialVersionUID = 1L;
  
          @Id
          @GeneratedValue(strategy= GenerationType.AUTO)
          private Integer id;
          private String name;
  
          @OneToMany(mappedBy="hotel")
          @JsonIgnore
          private Set&lt;Room&gt; rooms;
  
          public Hotel() {
          }
      // --- getters and setters omitted for brevity ---
      }
  
      @Entity
      @Table
      @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
      public class Room implements Serializable {
          private static final long serialVersionUID = 1L;
  
          @Id
          @GeneratedValue(strategy= GenerationType.AUTO)
          private Integer id;
  
  
          @ManyToOne(fetch= FetchType.LAZY)
          @JsonIdentityReference(alwaysAsId=true)
          private Hotel hotel;
  
          public Room() {
          }
      // --- getters and setters omitted for brevity ---
      }
  
      public interface HotelRepo extends JpaRepository&lt;Hotel, Integer&gt; {
      }
      public interface RoomRepo extends JpaRepository&lt;Room, Integer&gt; {
      }
  
  
      @RunWith(SpringRunner.class)
      @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
      @AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
      public class JPACreationTester {
  
          static final String CREATE_HOTEL_URL = "/api/hotels";
          static final String CREATE_ROOM_URL = "/api/rooms";
  
          @Autowired
          private TestRestTemplate restTemplate;
  
          @Test
          public void testDataCreation () throws Exception {
  
              String resName1 = "hotel1";
              Hotel hotel1 = new Hotel(resName1);
              hotel1.setId(100);
  
              String roomName1 = "room1";
              Room room1 = new Room(roomName1);
              room1.setId(200);
              room1.setHotel(hotel1);
  
              Set&lt;Room&gt; rooms = new HashSet&lt;&gt;();
              rooms.add(room1);
              hotel1.setRooms(rooms);
  
              Hotel hotel1saved = this.restTemplate.postForObject(CREATE_HOTEL_URL, hotel1, Hotel.class);
              Room room1saved = this.restTemplate.postForObject(CREATE_ROOM_URL, room1, Room.class);
              assertThat(room1saved).as("Rooms are equal").isEqualToIgnoringNullFields(room1);
              assertThat(hotel1saved).as("Hotels are equal").isEqualToIgnoringNullFields(hotel1);
          }
      }
  </code></pre>
  
  <p>Server log below</p>
  
  <pre><code>2017-01-13 11:51:47.969 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : Created POST request for "http://localhost:49515/api/hotels"
     2017-01-13 11:51:48.073 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : Setting request Accept header to [application/json, application/json, application/*+json, application/*+json]
     2017-01-13 11:51:48.090 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : Writing [com.example.entities.Hotel@1d9bd1d6] using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@10bdfbcc]
     Method: POST
     URI: http://localhost:49515/api/hotels
     Request Body: {"id":100,"name":"hotel1"}
     Response body: java.io.ByteArrayInputStream@23708f14
     2017-01-13 11:51:48.653 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : POST request for "http://localhost:49515/api/hotels" resulted in 201 (null)
     2017-01-13 11:51:48.655 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : Reading [class com.example.entities.Hotel] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@10bdfbcc]
     2017-01-13 11:51:48.656 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : Created POST request for "http://localhost:49515/api/rooms"
     2017-01-13 11:51:48.659 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : Setting request Accept header to [application/json, application/json, application/*+json, application/*+json]
     2017-01-13 11:51:48.660 DEBUG 1217 --- [           main] o.s.web.client.RestTemplate              : Writing [com.example.entities.Room@151659dd] using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@10bdfbcc]
     Method: POST
     URI: http://localhost:49515/api/rooms
     Request Body: {"id":200,"hotel":100,"name":"room1"}
     2017-01-13 11:51:48.697 ERROR 1217 --- [o-auto-1-exec-2] o.s.d.r.w.RepositoryRestExceptionHandler : Could not read document: Failed to convert from type [java.net.URI] to type [com.example.entities.Hotel] for value '100'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 100. Is it local or remote? Only local URIs are resolvable. (through reference chain: com.example.entities.Room["hotel"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to convert from type [java.net.URI] to type [com.example.entities.Hotel] for value '100'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 100. Is it local or remote? Only local URIs are resolvable. (through reference chain: com.example.entities.Room["hotel"])
  
     org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Failed to convert from type [java.net.URI] to type [com.example.entities.Hotel] for value '100'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 100. Is it local or remote? Only local URIs are resolvable. (through reference chain: com.example.entities.Room["hotel"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Failed to convert from type [java.net.URI] to type [com.example.entities.Hotel] for value '100'; nested exception is java.lang.IllegalArgumentException: Cannot resolve URI 100. Is it local or remote? Only local URIs are resolvable. (through reference chain: com.example.entities.Room["hotel"])
       at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:240) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readInternal(AbstractJackson2HttpMessageConverter.java:217) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:193) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.read(PersistentEntityResourceHandlerMethodArgumentResolver.java:228) ~[spring-data-rest-webmvc-2.5.6.RELEASE.jar:na]
       at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.read(PersistentEntityResourceHandlerMethodArgumentResolver.java:185) ~[spring-data-rest-webmvc-2.5.6.RELEASE.jar:na]
       at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:138) ~[spring-data-rest-webmvc-2.5.6.RELEASE.jar:na]
       at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:160) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129) ~[spring-web-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
       at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.5.RELEASE.jar:4.3.5.RELEASE]
  </code></pre>