Thursday, July 13, 2017

Marshalling two similar json fields to the same java field

Leave a Comment

I have a sample dummy JSON response that looks like :

    {         "id": 1,         "teacher_name": "Foo",         "teacher_address": "123 Main St.",         "teacher_phone_num": 1234567891,         "student_name": "Bar",         "student_address": "546 Main St.",         "student_phone_num": 9184248576     } 

The above is a silly example, but it helps illustrate the issue I am having trying to de-serialize the above into a Java class called "Employee" using Jackson:

public class Employee {     String name;     String address;     String phoneNumber; } 

The issue is that the JSON has two different prepends so I cannot annotate each field in Employee and have the object mapper map teacher_name and student_name to the name field in an Employee object. Is there a way in Jackson to specify two differently named nodes to map to the same Java field?

4 Answers

Answers 1

So in my example, I should end up with two Employee objects (I am guaranteed to have one pair per response)

It is not possible with Jackson. It is designed to map one-to-one: one json object to one java object. But you want to end up with two java objects from one json.

I would recommend you to go with strait forward way by implementing some processing level that will consume Response and map it to two Employee objects.

Answers 2

I think you need to write code to do the mapping whether you use annotation or not.

Simple is the best. If you read the json as JsonNode it should be trivial to code the assignments.

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import java.io.IOException;  public class Q44094751 {     public static void main(String[] args) throws IOException {       String json = "{\"id\": 1," +             "\"teacher_name\": \"Foo\", \"teacher_address\": \"123 Main St.\", \"teacher_phone_num\": 1234567891," +             "\"student_name\": \"Bar\", \"student_address\": \"546 Main St.\", \"student_phone_num\": 9184248576" +          "}";       ObjectMapper mapper = new ObjectMapper();       // Read       JsonNode node = mapper.readValue( json, JsonNode.class);       Employee teacher = new Employee(),                student = new Employee();       teacher.name = node.get( "teacher_name" ).toString();       teacher.address = node.get( "teacher_address" ).toString();       teacher.phoneNumber = node.get( "teacher_phone_num" ).toString();       student.name = node.get( "student_name" ).toString();       student.address = node.get( "student_address" ).toString();       student.phoneNumber = node.get( "student_phone_num" ).toString();       // Check       mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);       System.out.println( mapper.writeValueAsString( teacher ) );    }     public static class Employee {       String name;       String address;       String phoneNumber;    } } 

Given a "silly example", the answer that may looks silly. For example if you have a hundred properties, a loop with reflection should work better.

If you have requirement(s) that is not reflected in the example, please edit your question to better describe the actual problem(s) you are facing.

Answers 3

I believe that it is not possible with Jackson, The workarounds that I can think of are

  1. Split it into 2 object Teacher and Student. You can still pass the same object twice but with different classes Teacher and Student, It works, but what about the id field?

  2. Make a Java class similar to JSON.

  3. If you would like to make it more meaning full structure then use this structure

    { "id": 1, "teacher" :{ "name": "Foo", "address": "123 Main St.", "phone_num": 1234567891, }, "student" :{ "name": "Bar", "address": "546 Main St.", "phone_num": 9184248576, } }

Answers 4

You may use @JsonUnwrapped annotation to unwrap the employee's properties inline in parent object. This annotation also provides the option to specify the prefix name of which will be used while ser/der the object.

Below is your example:

 public static class Employee {         private String name;         private String address;         @JsonProperty("phone_num")         private String phoneNumber;          // setter / getter removed from brevity     }      public static class YourJsonObject {         private int id;         @JsonUnwrapped(prefix="teacher_")         private Employee teacher;         @JsonUnwrapped(prefix = "student_")         private Employee student;          // setter / getter removed from brevity     } 

Now Configure the ObjectMapper to use the appropriate naming strategy( from your example,I see snake case strategy is desired)

Test Case:

   @Test     public void peformTest() throws Exception {         final String inputJson = "{\n" +                 "        \"id\": 1,\n" +                 "        \"teacher_name\": \"Foo\",\n" +                 "        \"teacher_address\": \"123 Main St.\",\n" +                 "        \"teacher_phone_num\": 1234567891,\n" +                 "        \"student_name\": \"Bar\",\n" +                 "        \"student_address\": \"546 Main St.\",\n" +                 "        \"student_phone_num\": 9184248576\n" +                 "    }";          ObjectMapper mapper = new ObjectMapper();         // important one         mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);          // read your json as object         YourJsonObject myObject = mapper.readValue(inputJson, YourJsonObject.class);          assertThat(myObject.getTeacher().getName(), is("Foo"));         assertThat(myObject.getStudent().getName(), is("Bar"));     } 

Hope this helps.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment