Search code examples
springresttemplate

How to parse json with mixed child objects using Sprint Rest Template


I have a json response which looks like below

{
  "resourceType": "Topic",
  "metadata": {
    "lastUpdated": "2016-12-15T14:51:33.490-06:00"
  },
  "entry": [
    {
      "resource": {
        "resourceType": "Outcome",
        "issue": [
          {
            "response": "error",
            "code": "exception"
          },
          {
            "response": "success",
            "code": "informational"
          },
		  {
            "response": "success",
            "code": "informational"
          }
        ]
      }
    },
    {
      "resource": {
        "resourceType": "Data",
        "id": "80",
        "subject": {
          "reference": "dataFor/80"
        },
        "created": "2016-06-23T04:29:00",
        "status": "current"
      }
    },
	{
      "resource": {
        "resourceType": "Data",
        "id": "90",
        "subject": {
          "reference": "dataFor/90"
        },
        "created": "2016-06-23T04:29:00",
        "status": "current"
      }
    }
  ]
}

Data and Outcome Class extends Resource.

I am using Spring RestTemplate.getForObject(url, someClass). I get below error

has thrown exception, unwinding now
org.apache.cxf.interceptor.Fault: Could not read JSON: Unrecognized field "response" (Class com.model.Resource), not marked as ignorable
at [Source: sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@77a3e67a; 

I understand that the json is not getting parsed to the child class of Resource. I want to do something like RestTemplate.getForObject(url, someClass) but this is not supported by java generics (wildcard). Please help


Solution

  • You'll want to use jackson to deserialize to a dynamic type, using resourceType as the field to indicate the actual type. Add these to your Resource class.

    @JsonTypeInfo(property = "resourceType", use = Id.NAME)
    @JsonSubTypes({ @Type(Data.class), 
                @Type(Outcome.class) 
              })
    

    Here is a unit test that will prove out the behavior.

    @Test
    public void deserializeJsonFromResourceIntoData () throws IOException {
       Data data = (Data) new ObjectMapper().readValue("{" +
                "        \"resourceType\": \"Data\"," +
                "        \"id\": \"80\"," +
                "        \"subject\": {" +
                "          \"reference\": \"dataFor/80\"" +
                "        }," +
                "        \"created\": \"2016-06-23T04:29:00\"," +
                "        \"status\": \"current\"" +
                "      }", Resource.class);
    
        assertEquals(Integer.valueOf(80), data.getId());
        assertEquals("dataFor/80", data.getSubject().getReference());
    }
    

    As for the cast, I've done it here just to demonstrate that it works, however, to be truly polymorphic, you probably want to have Resource contain all the behavior you need, and then everything is just a Resource.