jackson serializing Collections.unmodifiable*
collections, jackson, java, json, serialization
Solution
Okay, you've run into an edge-ish type case with Jackson. The problem really is that the library will happily use your getter method to retrieve collection and map properties, and only falls back to instantiating these collections/maps if those getter methods return null.
This can fixed by a combination of `@JsonProperty/@JsonIgnore` annotations, with the caveat that the `@class` property in your JSON output will change.
Code example:
public class Account {
@JsonProperty("memberEmails")
private Map<Integer, String> memberEmails = Maps.newHashMap();
public Account() {
super();
}
public void setMemberEmails(Map<Integer, String> memberEmails) {
this.memberEmails = memberEmails;
}
@JsonIgnore
public Map<Integer, String> getMemberEmails() {
return Collections.unmodifiableMap(memberEmails);
}
}
If you serialize this class with your test code you will get the following JSON:
{
"@class": "misc.stack.pojo.Account",
"memberEmails": {
"10": "bob@mail.com",
"@class": "java.util.HashMap"
}
}
Which will deserialize correctly.
Problem
I have a problem using the Jackson serialization from json, how do I serialize from `Collections.unmodifiableMap?` The error I get is: ``` com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of java.util.Collections$UnmodifiableMap, problem: No default constructor found ``` I wanted to use the `SimpleAbstractTypeResolver` from http://wiki.fasterxml.com/SimpleAbstractTypeResolver however I cannot get the inner class type `Collections$UnmodifiableMap` ``` Map<Integer, String> emailMap = newHashMap(); Account testAccount = new Account(); ObjectMapper mapper = new ObjectMapper(); mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, As.PROPERTY); String marshalled ; emailMap.put(Integer.valueOf(10), "bob@mail.com"); testAccount.setMemberEmails(emailMap); marshalled = mapper.writeValueAsString(testAccount); System.out.println(marshalled); Account returnedAccount = mapper.readValue(marshalled, Account.class); System.out.println(returnedAccount.containsValue("bob@mail.com")); public class Account { private Map<Integer, String> memberEmails = Maps.newHashMap(); public void setMemberEmails(Map<Integer, String> memberEmails) { this.memberEmails = memberEmails; } public Map<Integer, String> getMemberEmails() { return Collections.unmodifiableMap(memberEmails); } ``` Any ideas? Thanks in advance.