Mridang Agarwalla
Mridang Agarwalla

Reputation: 44978

Why can't I unwrap the root node and deserialize an array of objects?

Why am I not able to deserialize an array of objects by unwrapping the root node?

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonRootName;
import org.junit.Assert;
import org.junit.Test;

public class RootNodeTest extends Assert {

    @JsonRootName("customers")
    public static class Customer {
        public String email;
    }

    @Test
    public void testUnwrapping() throws IOException {
        String json = "{\"customers\":[{\"email\":\"[email protected]\"},{\"email\":\"[email protected]\"}]}";
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationConfig.Feature.UNWRAP_ROOT_VALUE, true);
        List<Customer> customers = Arrays.asList(mapper.readValue(json, Customer[].class));
        System.out.println(customers);
    }
}

I've been digging through the Jackson documentation and this is what I could figure out but upon running it, I get the following error:

A org.codehaus.jackson.map.JsonMappingException has been caught, Root name 'customers' does not match expected ('Customer[]') for type [array type, component type: [simple type, class tests.RootNodeTest$Customer]] at [Source: java.io.StringReader@49921538; line: 1, column: 2]

I would like to accomplish this without creating a wrapper class. While this is an example, I don't want to create unnecessary wrapper classes only for unwrapping the root node.

Upvotes: 0

Views: 4926

Answers (3)

Arthur Eirich
Arthur Eirich

Reputation: 3638

This code worked for me:

import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

public class RootNodeTest extends Assert {

public static class CustomerMapping {
    public List<Customer> customer;

    public List<Customer> getCustomer() {
        return customer;
    }

    public static class Customer {
        public String email;

        public String getEmail() {
            return email;
        }
    }

}

@Test
public void testUnwrapping() throws IOException {
    String json = "{\"customer\":[{\"email\":\"[email protected]\"},{\"email\":\"[email protected]\"}]}";
    ObjectMapper mapper = new ObjectMapper();
    CustomerMapping customerMapping = mapper.readValue(json, CustomerMapping.class);
    List<CustomerMapping.Customer> customers = customerMapping.getCustomer();
    for (CustomerMapping.Customer customer : customers) {
        System.out.println(customer.getEmail());
    }
  }
}

First of all you need a java object for the whole json object. In my case this is CustomerMapping. Then you need a java object for your customer key. In my case this is the inner class CustomerMapping.Customer. Because customer is a json array you need a list of CustomerMapping.Customer objects. Also, you do not need to map the json array to a java array and convert it then to a list. Jackson already does it for you. Finally, you just specify the variable email of type String and print it to the console.

Upvotes: -1

araqnid
araqnid

Reputation: 133482

Create an ObjectReader to configure the root name explicitly:

@Test
public void testUnwrapping() throws IOException {
    String json = "{\"customers\":[{\"email\":\"[email protected]\"},{\"email\":\"[email protected]\"}]}";
    ObjectReader objectReader = mapper.reader(new TypeReference<List<Customer>>() {})
                                      .withRootName("customers");
    List<Customer> customers = objectReader.readValue(json);
    assertThat(customers, contains(customer("[email protected]"), customer("[email protected]")));
}

(btw this is with Jackson 2.5, do you have a different version? I have DeserializationFeature rather than DeserializationConfig.Feature)

It seems that by using an object reader in this fashion, you don't need to globally configure the "unwrap root value" feature, nor use the @JsonRootName annotation.

Note also that you can directly request a List<Customer> rather than going through an array- the type given to ObjectMapper.reader works just like the second parameter to ObjectMapper.readValue

Upvotes: 7

Sharon Ben Asher
Sharon Ben Asher

Reputation: 14328

it seems you can't escape a wrapper class. according to this, the @JsonRootName annotation will only allow you to unwrap a json that contains a single instance of your pojo: so it will work for a String like this: "{\"customer\":{\"email\":\"[email protected]\"}}";

Upvotes: -1

Related Questions