I would like to be able to test if a List contain an object with a given key-value
For example, I would like to do something like Iterables.contains(l2, "lname", "Jordan")); instead of having to create all other Map objects like below in l2
//List<String> l = Arrays.asList("Mickael", "Jordan", "His Airness");
//System.out.println(Iterables.contains(l, "Jordan"));
Map<String, String> p1 = new HashMap<String, String>();
p1.put("fname", "Mickael");
p1.put("lname", "Jordan");
p1.put("nname", "His Airness");
Map<String, String> p2 = new HashMap<String, String>();
p2.put("fname", "Paul");
p2.put("lname", "Pierce");
p2.put("nname", "The Truth");
List<Map<String, String>> l2 = Arrays.asList(p1, p2);
Map<String, String> p3 = new HashMap<String, String>();
p3.put("fname", "Mickael"); //
p3.put("lname", "Jordan");
p3.put("nname", "His Airness"); //
System.out.println(Iterables.contains(l2, p3));
I'd like to know if there's such guava's function, and not doing a loop on l2 and testing each elt.get("lname")
Edit
3 solutions answered: trying to see which one is more perfomant
System.out.println(Iterables.any(l2, withEntry("lname", "Jordan"))); //#axtavt
System.out.println(has("lname", "Jordan")); //#JB
System.out.println(Iterables.any(l2, new KeyValuePredicate("lname", "Jordan"))); //#JB
public static Boolean has(final String key, final String value) {
return Iterables.any(l2, new Predicate<Map<String, String>>() {
#Override
public boolean apply(Map<String, String> input) {
return input.get(key).equals(value);
}
});
}
public static Predicate<Map<String, String>> withEntry(final String key, final String value) {
return new Predicate<Map<String, String>>() {
public boolean apply(Map<String, String> input) {
return value.equals(input.get(key));
}
};
}
class KeyValuePredicate implements Predicate<Map<String, String>>{
private String key;
private String value;
public KeyValuePredicate(String key, String value) {
super();
this.key = key;
this.value = value;
}
#Override
public boolean apply(Map<String, String> arg0) {
// TODO Auto-generated method stub
return arg0.get(key).equals(value);
}
}
return Iterables.any(l2, new Predicate<Map<String, String>>() {
#Override
public boolean apply(Map<String, String> input) {
return input.get("lname").equals("Jordan");
}
});
But you're using maps when you should use objects with properties.
Of course, if you need to do that multiple times, with various properties, you should transform the predicate into a non-anonymous, reusable class:
return Iterables.any(l2, new KeyValuePredicate("lname", "Jordan"));
You can implement an appropriate Predicate and use Iterables.any():
public Predicate<Map<String, String>> withEntry(final String key, final String value) {
return new Predicate<Map<String, String>>() {
public boolean apply(Map<String, String> input) {
return value.equals(input.get(key));
}
};
}
System.out.println(Iterables.any(l2, withEntry("lname", "Jordan")));
Well this is straightforward.
You should create a proper entity class:
public class Person {
private String fName;
private String lName;
private String nName;
public Person(String fName, String lName, String nName) {
this.fName = fName;
this.lName = lName;
this.nName = nName;
}
public String getFName() {
return fName;
}
public String getLName() {
return lName;
}
public String getNName() {
return nName;
}
}
Then you can do the following:
import java.util.*;
public class Test {
public static void main (String [] args) {
List<Person> list = new ArrayList<Person>();
Person p1 = new Person("Mickael", "Jordan", "His Airness");
for (Person person : list) {
if (person.getFName().equals("Mickael")) {
System.out.println("Mickael is in the list!");
break;
}
}
}
}
Related
I have hashmap string object below. How to get value name from object SubSource.value index 0? I just found function for get first object, for example I just get value from test with hashMapValue.get("test"). How to get value object inside the object? should I convert to json and I get the value? Thanks.
{
"test" : {
"type" : "",
"value" : ""
},
"Attachment" : {
"type" : "",
"value" : ""
},
"SubSource" : {
"type" : "string",
"value" : [ {
"address" : "xxx.xxxx#xxx.co.id",
"name" : "bobby"
}, {
"address" : "xxx.xxxx#xxx.co.id",
"name" : "2sadasd"
}, {
"address" : "xxx.xxxx#xxx.co.id",
"name" : "ggfgf"
} ]
}
}
My code:
Map<String, Object> departmentPHSSuportEmail = new HashMap<String, Object>();
Map<String, Object> subSourceMap = null;
List<Map<String , Object>> myMap = new ArrayList<Map<String,Object>>();
Map<String, Object> attachment = new HashMap<String, Object>();
attachment.put("type", "");
attachment.put("value", "");
departmentPHSSuportEmail.put("Attachment", attachment);
Map<String, Object> subSource = new HashMap<String, Object>();
subSource.put("type", "string");
subSource.put("value", myMap);
departmentPHSSuportEmail.put("SubSource", subSource);
// create a fresh map
Map<String,Object> subSourceMap1 = new HashMap<>();
subSourceMap1.put("name", "bobby");
subSourceMap1.put("address", "xxx.xxxx#xxx.co.id");
// create a fresh map
Map<String,Object> subSourceMap2 = new HashMap<>();
subSourceMap2.put("name", "2sadasd");
subSourceMap2.put("address", "xxx.xxxx#xxx.co.id");
// create a fresh map
Map<String,Object> subSourceMap3 = new HashMap<>();
subSourceMap3.put("name", "ggfgf");
subSourceMap3.put("address", "xxx.xxxx#xxx.co.id");
myMap.add(subSourceMap1);
myMap.add(subSourceMap2);
myMap.add(subSourceMap3);
Map<String, Object> attachments = new HashMap<String, Object>();
attachments.put("type", "");
attachments.put("value", "dasda");
departmentPHSSuportEmail.put("test", attachments);
Not sure what you are asking, but...
A) Cast the object you are getting from the map to the Object type you are trying to grab the values out of
String name = (String) subSourceMap1.get("name");
B) Add type parameters to your map
Map<String, String> subSourceMap1 = new HashMap<String, String>();
String name = subSourceMap1.get("name");
String address = subSourceMap1.get("address");
C) If you are wondering how to get those maps out of a list
Map<String, YourObject> subSourceMap1 = myMap.get(0); //This is index 0's of your map subsource
//You can grab index's from 'myMap' that are less than myMap.size();
This is a JSON string. Find a proper JSON deserializer library and include that in your project instead of coding all this HashMap stuff.. ;)
Ie this
jackson-2-convert-object-to-from-json
Map<String, Object> subSource2 = (Map<String, Object>)departmentPHSSuportEmail.get("SubSource");
List<Map<String , Object>> myMap2 = (List<Map<String , Object>>)subSource2.get("value");
Map<String,Object> subSourceMap3 = myMap2.get(0);
String value = (String)subSourceMap3.get("value");
There are many ways how to solve this, one of solutions can be like this:
make container for member data (address + name), if the addresses
should be unique, than can be used as a key in map
make container for attachment
make container for the whole data, because of data integrity
Please, notice, I have used hashmap for members, but arraylist for attachments, if you are expecting also unique values at attachments, than you can use also hashmap with key name, then you can use it for finding
So..
Member container
public class Member {
private String name;
private String address;
public Member(String name, String address) {
super();
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
Container for attachments
public class Attachment {
String name;
String value;
public Attachment(String name, String value) {
super();
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
Than container for complete data
public class DataContainer {
Map<String, Member> membersMap;
ArrayList<Attachment> attachments;
public DataContainer() {
this.membersMap = new HashMap<String, Member>();
this.attachments = new ArrayList<Attachment>();
}
public DataContainer(Map<String, Member> membersMap, ArrayList<Attachment> attachments) {
super();
this.membersMap = membersMap;
this.attachments = attachments;
}
public Map<String, Member> getMembersMap() {
return membersMap;
}
public void setMembersMap(Map<String, Member> membersMap) {
this.membersMap = membersMap;
}
public ArrayList<Attachment> getAttachments() {
return attachments;
}
public void setAttachments(ArrayList<Attachment> attachments) {
this.attachments = attachments;
}
public void addAttachment(Attachment newItem) {
this.attachments.add(newItem);
}
public void addMember(Member newMember) {
this.membersMap.put(newMember.getAddress(), newMember);
}
public Member getMemberByAddress(String address) {
return this.membersMap.get(address);
}
}
Filling example
Map<String, Member> membersMap= new HashMap<String, Member>();
Member newMember = new Member("fooo Name", "fooo#whatever.foo");
membersMap.put(newMember.getAddress(), newMember);
Attachment newAtatchment = new Attachment("fooattach", "something");
ArrayList<Attachment> attachments = new ArrayList<>();
attachments.add(newAtatchment);
DataContainer data = new DataContainer(membersMap, attachments);
data.addMember(new Member("anotherMember", "fooo#foooo.foo"));
data.addAttachment(new Attachment("another attachmetn", "anotherAtt value"));
I know this question is kind of repetitive But still I can't get an answer.
I have 2 arraylists containing name and description respectively as
final ArrayList<String> arrayOne = new ArrayList<String>(); // containing names
final ArrayList<String> arraytwo = new ArrayList<String>(); // containing description
I need a view like
I have tried
arraytwo.add(arrayOne);
&
arrayThree.addAll(arrayOne);
arrayThree.addAll(arrayTwo);
But can't a desired arraylist.
Regards
POJO class
public class Model
{
String name;
String desc;
public String getName() {
return name;
}
public void setName(String name) {
this.name= name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
desc = desc;
}
}
For Storing to arraylist
ArrayList<Model> arrayModel = new ArrayList<Model>();
for(int i=0;i<arrayOne.size();i++)
{
Model model=new Model();
model.setName(arrayOne.get(i));
model.setDesc(arrayTwo.get(i));
arrayModel.add(model);
}
If you don't want to create POJO, try this:
List<String> nameList;
List<String> desList;
//for storing
Map<String, String> map = new HashMap<>();
for(int i=0;i<nameList.size();i++) {
map.put(nameList.get(i), desList.get(i));
}
//for retrieving
for(Map.Entry<String, String> m : map.entrySet())
String nameListItem = m.getKey();
String desListItem = m.getValue();
}
I am building a rest API using Jersey where XML and JSON outputs are allowed depending on what format client prefers(using Accept header).The service sends the below class as an output which looks like this
#XmlRootElement
public class ProjectDetails{
private List<Attachment> attachments;
private Map<String, List<Attachment>> imageCategory;
#XmlTransient
public List<Attachment> getAttachments() {
return attachments;
}
public void setAttachments(List<Attachment> attachments) {
this.attachments = attachments;
}
public Map<String, List<Attachment>> getImageCategory() {
if(attachments == null || attachments.size() == 0){
return null;
}
Map<String, List<Attachment>> map = new HashMap<String, List<Attachment>>();
for (Attachment img : attachments){
String key = img.getCategory();
if(BaseUtil.hasText(key)){
List<Attachment> values = map.get(key);
if (values == null){
values = new ArrayList<Attachment>();
}
values.add(img);
map.put(key, values);
}
}
this.imageCategory = map ;
return imageCategory;
}
public void setImageCategory(Map<String, List<Attachment>> imageCategory) {
this.imageCategory = imageCategory;
}
}
I don't want attachments field as an output so marked it with #XmlTransient rather I want to form a Map using the attachments field and send it as an output.
In case of JSON format, I am getting the correct response.But in case of XML, I am not getting any output when I hit the service.
I think it is related to this Map field because if I remove Map field and add some other field like String then I get that field in response.
Please let me know how to resolve this.
Update:
After some googling, i found XmlAdapter solution and implemented as below
public class MapAdapter extends
XmlAdapter<MapAdapter.AdaptedMap, Map<String, List<Attachment>>> {
public static class AdaptedEntry {
public String key;
public List<Attachment> value = new ArrayList<Attachment>();
}
public static class AdaptedMap {
List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}
#Override
public AdaptedMap marshal(Map<String, List<Attachment>> map)
throws Exception {
AdaptedMap adaptedMap = new AdaptedMap();
for (Entry<String, List<Attachment>> entry : map.entrySet()) {
AdaptedEntry adaptedEntry = new AdaptedEntry();
adaptedEntry.key = entry.getKey();
adaptedEntry.value = entry.getValue();
adaptedMap.entries.add(adaptedEntry);
}
return adaptedMap;
}
#Override
public Map<String, List<Attachment>> unmarshal(AdaptedMap adaptedMap)
throws Exception {
List<AdaptedEntry> adapatedEntries = adaptedMap.entries;
Map<String, List<Attachment>> map = new HashMap<String, List<Attachment>>(
adapatedEntries.size());
for (AdaptedEntry adaptedEntry : adapatedEntries) {
map.put(adaptedEntry.key, adaptedEntry.value);
}
return map;
}
And then
#XmlJavaTypeAdapter(MapAdapter.class)
public Map<String, String> getImageCategory() {
But still it's not working..Anything I missed?
I have used your ProjectDetails class a little bit changes I've made, and it provides response for both XML and JSON. Can you try this?
#XmlRootElement
public class ProjectDetails {
private List<Attachment> attachments;
private Map<String, ArrayList<Attachment>> imageCategory;
#XmlTransient
public List<Attachment> getAttachments() {
return attachments;
}
public void setAttachments(List<Attachment> attachments) {
this.attachments = attachments;
}
#XmlJavaTypeAdapter(MapAdapter.class)
public Map<String, ArrayList<Attachment>> getImageCategory() {
if(attachments == null || attachments.size() == 0){
return null;
}
Map<String, ArrayList<Attachment>> map = new HashMap<String, ArrayList<Attachment>>();
for (Attachment img : attachments){
String key = img.getCategory();
if(!key.equals("")){
ArrayList<Attachment> values = map.get(key);
if (values == null){
values = new ArrayList<Attachment>();
}
values.add(img);
map.put(key, values);
}
}
this.imageCategory = map ;
return imageCategory;
}
public void setImageCategory(Map<String, ArrayList<Attachment>> imageCategory) {
this.imageCategory = imageCategory;
}
}
And the Adapter class you can use the following
public class MapAdapter extends XmlAdapter<MapElement[], Map<String, ArrayList<Attachment>>>{
public MapElement[] marshal(Map<String, ArrayList<Attachment>> arg0) throws Exception {
MapElement[] mapElements = new MapElement[arg0.size()];
int i = 0;
for (Map.Entry<String, ArrayList<Attachment>> entry : arg0.entrySet()){
mapElements[i++] = new MapElement(entry.getKey(), entry.getValue());
}
return mapElements;
}
public Map<String, ArrayList<Attachment>> unmarshal(MapElement[] arg0) throws Exception {
Map<String, ArrayList<Attachment>> r = new HashMap<String, ArrayList<Attachment>>();
for (MapElement mapelement : arg0)
r.put(mapelement.key, mapelement.value);
return r;
}
}
I've changed the MapElement also
public class MapElement {
#XmlElement
public String key;
#XmlElement
public ArrayList<Attachment> value;
private MapElement() {
}
public MapElement(String key, ArrayList<Attachment> value) {
this.key = key;
this.value = value;
}
}
And the Attachement class should have getter setter methods
public class Attachment {
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
private String category;
public Attachment(String cat){
this.category = cat;
}
}
Following question has been separated from this one:
ArrayIndexOutOfBoundsException while Spring save data to MongoDB
I have problem with saving Object to MongoDB. I've noticed that problem might be caused by too complex object. I have following class hierarchy:
ClassA is superclass for ClassB and ClassC. ClassD contains map of maps. ClassC contains ClassB.
Code which I invoke is following:
ClassC c = new ClassC()
c.setName("NAME");
mongoOperation.save(c, "Mongo"); // MongoOperations object
The problem is that Mongo doesn't save object's data. It saves only _id and _class.
Actual data
{
"_id" : ObjectId("53e86cd9c506f66eafaa03cb"),
"_class" : "com.sample.ClassC"
}
Expected data
{
"_id" : ObjectId("53e86cd9c506f66eafaa03cb"),
"_class" : "com.sample.ClassC",
"name" : "NAME"
}
Funny thing is that when I comment out map field in ClassD everything works fine.
Is it possible to be caused by too complex object which I try to serialize?
EDIT
When I remove bObject from ClassC it also works fine.
EDIT 2
All classes are simple beans with setters and getters.
e.g.
public class ClassD{
private TreeMap<String, TreeMap<String,String>> map;
public TreeMap<String, TreeMap<String, String>> getMap() {
return map;
}
public void setMap(TreeMap<String, TreeMap<String, String>> map) {
this.map = map;
}
}
EDIT 3
Full example below, it has same class hierarchy as picture above.
public class Application implements CommandLineRunner {
#Autowired
private MongoTemplate mongoTemplate;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) throws Exception {
ClassC cObject = new ClassC();
cObject.setName("Jon");
try {
mongoTemplate.save(cObject);
}catch(Exception e){
e.printStackTrace();
}
mongoTemplate.save(cObject);
}
}
class ClassA{
private String name;
private ClassD dObject;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ClassD getdObject() {
return dObject;
}
public void setdObject(ClassD dObject) {
this.dObject = dObject;
}
}
class ClassB extends ClassA {
}
class ClassC extends ClassA {
private ClassB b;
public ClassB getB() {
return b;
}
public void setB(ClassB b) {
this.b = b;
}
}
class ClassD {
private TreeMap<String, TreeMap<String, String>> map = new TreeMap<>();
public TreeMap<String, TreeMap<String, String>> getMap() {
return map;
}
public void setMap(TreeMap<String, TreeMap<String, String>> map) {
this.map = map;
}
}
I guess the MongoConverter in specific version of your spring-data-mongodb.jar works incorrectly.
Spring must convert your ClassC instance into DBObject format, then call DBCollection.save to save data into database. You can check the content of DBObject parameter in method "com.mongodb.DBCollection.save" whether it contains correct data as you expect.
I copy your ClassC with complete structure and test, it's fine and cannot reproduce what you described above. I use spring-data-mongdb-1.2.3-RELEASE.jar. What's the version you adopt?
The following code seems to work:
#EnableAutoConfiguration
public class Application implements CommandLineRunner {
#Autowired
private MongoTemplate mongoTemplate;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
public void run(String... args) throws Exception {
Customer customer = new Customer("myself");
ClassB classB = new ClassB();
TreeMap<String, TreeMap<String, String>> map = new TreeMap<String, TreeMap<String, String>>();
TreeMap<String, String> innermap = new TreeMap<String, String>();
innermap.put("iam", "cool");
map.put("innermap", innermap);
TreeMap<String, String> innermap2 = new TreeMap<String, String>();
innermap2.put("youare", "yellow");
map.put("innermap2", innermap2);
classB.setMap(map);
customer.setClassB(classB);
try {
mongoTemplate.save(customer);
} catch (Exception e) {
e.printStackTrace();
}
mongoTemplate.save(customer);
System.out.println(mongoTemplate.findAll(Customer.class));;
}
}
public class ClassB {
private TreeMap<String, TreeMap<String, String>> map = new TreeMap<String, TreeMap<String, String>>();
public TreeMap<String, TreeMap<String, String>> getMap() {
return map;
}
public void setMap(TreeMap<String, TreeMap<String, String>> map) {
this.map = map;
}
}
#Document(collection ="customer")
public class Customer {
#Id
private String id;
private String name;
private ClassB classB;
public Customer() {
}
public Customer(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ClassB getClassB() {
return classB;
}
public void setClassB(ClassB classB) {
this.classB = classB;
}
#Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", classB=" + classB
+ "]";
}
}
But the ArrayIndexOutOfBoundsException-issue is still present.
I would like deserialize my custom serialized objects. My objects are basically consisting a simple Pair implementation.
class School{
Integer id;
String schoolName;
}
class Student{
Integer id;
Integer schoolId;
String studentName;
}
#JsonSerialize(using=PairSerializer.class)
public class Pair<V,K>{
V v;
K k;
}
Here is the result
[
{
"v":{
"id":1,
"schoolId":3,
"studentName":"O. Bas"
},
"k":{
"id":3,
"schoolName":"School 3"
}
},
{
"v":{
"id":2,
"schoolId":3,
"studentName":"C. Koc"
},
"k":{
"id":3,
"schoolName":"School 3"
}
}
]
v and k as field name in json is pretty ugly. That is why I have written a custom serializer as this:
#Override
public void serialize(Pair pair, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeStartObject();
jsonGenerator.writeObjectField(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL,pair.getK().getClass().getSimpleName() ), pair.getK());
jsonGenerator.writeObjectField(CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL,pair.getV().getClass().getSimpleName() ), pair.getV());
jsonGenerator.writeEndObject();
}
The result is exactly what I want. v and k field names are replaced by their class names.
[
{
"school":{
"id":3,
"schoolName":"School 3"
},
"student":{
"id":1,
"schoolId":3,
"studentName":"O. Bas"
}
},
{
"school":{
"id":3,
"schoolName":"School 3"
},
"student":{
"id":2,
"schoolId":3,
"studentName":"C. Koc"
}
}
]
Here is the my question. How can I deserialize my json string to List<Pair<V, K> ? The real problem is that V and K are depends on the deserialized context it might vary as Student, School or another pair implementation.
public class PairDeserializer extends JsonDeserializer<Pair> {
public PairDeserializer() {
}
#Override
public Pair deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// I need to Deserialized generic type information of Pair
}
}
I think, you should create your own PropertyNamingStrategy. For example see my simple implementation:
class MapTransformNamingStrategy extends LowerCaseWithUnderscoresStrategy {
private static final long serialVersionUID = 1L;
private Map<String, String> mapping;
public MapTransformNamingStrategy(Map<String, String> mapping) {
this.mapping = mapping;
}
#Override
public String translate(String property) {
if (mapping.containsKey(property)) {
return mapping.get(property);
}
return property;
}
}
Now you can use it in this way:
Map<String, String> mapping = new HashMap<String, String>();
mapping.put("k", "student");
mapping.put("v", "school");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(new MapTransformNamingStrategy(mapping));
//etc
Example JSON output:
{ "school" : { "id" : 1,
"schoolName" : "The Best School in the world"
},
"student" : { "id" : 1,
"schoolId" : 1,
"studentName" : "Arnold Shwarz"
}
}
EDIT
Because my answer is not clear for everyone I present full example source code which serialize Java POJO objects into JSON and "vice versa".
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy.LowerCaseWithUnderscoresStrategy;
public class JacksonProgram {
#SuppressWarnings("unchecked")
public static void main(String[] args) throws Exception {
List<Pair<Student, School>> pairs = createDataForSerialization();
Map<String, String> mapping = createSchoolStudentMapping();
JsonConverter jsonConverter = new JsonConverter(mapping);
String json = jsonConverter.toJson(pairs);
System.out.println("JSON which represents list of pairs:");
System.out.println(json);
List<Pair<Student, School>> value = jsonConverter.fromJson(json, List.class);
System.out.println("----");
System.out.println("Deserialized version:");
System.out.println(value);
}
private static Map<String, String> createSchoolStudentMapping() {
Map<String, String> mapping = new HashMap<String, String>();
mapping.put("k", "student");
mapping.put("v", "school");
return mapping;
}
private static List<Pair<Student, School>> createDataForSerialization() {
List<Pair<Student, School>> pairs = new ArrayList<Pair<Student, School>>();
pairs.add(new Pair<Student, School>(new Student(1, 3, "O. Bas"), new School(3, "School 3")));
pairs.add(new Pair<Student, School>(new Student(2, 4, "C. Koc"), new School(4, "School 4")));
return pairs;
}
}
class JsonConverter {
private Map<String, String> mapping;
private ObjectMapper objectMapper;
private JsonFactory jsonFactory;
public JsonConverter(Map<String, String> mapping) {
this.mapping = mapping;
initJsonObjects();
}
private void initJsonObjects() {
objectMapper = new ObjectMapper();
objectMapper.setPropertyNamingStrategy(new MapTransformNamingStrategy(mapping));
jsonFactory = new JsonFactory();
}
public String toJson(Object object) throws Exception {
StringWriter stringWriter = new StringWriter();
JsonGenerator jsonGenerator = jsonFactory.createGenerator(stringWriter);
objectMapper.writeValue(jsonGenerator, object);
return stringWriter.toString();
}
public <T> T fromJson(String json, Class<T> expectedType) throws Exception {
JsonParser jsonParser = jsonFactory.createJsonParser(json);
return objectMapper.readValue(jsonParser, expectedType);
}
}
class MapTransformNamingStrategy extends LowerCaseWithUnderscoresStrategy {
private static final long serialVersionUID = 1L;
private Map<String, String> mapping;
public MapTransformNamingStrategy(Map<String, String> mapping) {
this.mapping = mapping;
}
#Override
public String translate(String property) {
if (mapping.containsKey(property)) {
return mapping.get(property);
}
return property;
}
}
class School {
private Integer id;
private String schoolName;
public School() {
}
public School(Integer id, String schoolName) {
this.id = id;
this.schoolName = schoolName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSchoolName() {
return schoolName;
}
public void setSchoolName(String schoolName) {
this.schoolName = schoolName;
}
#Override
public String toString() {
return "School [id=" + id + ", schoolName=" + schoolName + "]";
}
}
class Student {
private Integer id;
private Integer schoolId;
private String studentName;
public Student() {
}
public Student(Integer id, Integer schoolId, String studentName) {
this.id = id;
this.schoolId = schoolId;
this.studentName = studentName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getSchoolId() {
return schoolId;
}
public void setSchoolId(Integer schoolId) {
this.schoolId = schoolId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
#Override
public String toString() {
return "Student [id=" + id + ", schoolId=" + schoolId + ", studentName=" + studentName
+ "]";
}
}
class Pair<V, K> {
private V v;
private K k;
public Pair() {
}
public Pair(V v, K k) {
this.v = v;
this.k = k;
}
public V getV() {
return v;
}
public void setV(V v) {
this.v = v;
}
public K getK() {
return k;
}
public void setK(K k) {
this.k = k;
}
#Override
public String toString() {
return "Pair [v=" + v + ", k=" + k + "]";
}
}
The full output log:
JSON which represents list of pairs:
[{"school":{"id":1,"schoolId":3,"studentName":"O. Bas"},"student":{"id":3,"schoolName":"School 3"}},{"school":{"id":2,"schoolId":4,"studentName":"C. Koc"},"student":{"id":4,"schoolName":"School 4"}}]
----
Deserialized version:
[{school={id=1, schoolId=3, studentName=O. Bas}, student={id=3, schoolName=School 3}}, {school={id=2, schoolId=4, studentName=C. Koc}, student={id=4, schoolName=School 4}}]
Because the output JSON is not formatted I present it in more understandable version:
[
{
"school":{
"id":1,
"schoolId":3,
"studentName":"O. Bas"
},
"student":{
"id":3,
"schoolName":"School 3"
}
},
{
"school":{
"id":2,
"schoolId":4,
"studentName":"C. Koc"
},
"student":{
"id":4,
"schoolName":"School 4"
}
}
]
As you can see, we create new JsonConverter object with definition of mapping between Pair property names and which names we want to see in JSON string representation. Now if you have for example Pair<School, Room> you can create mapping Map in this way:
private static Map<String, String> createSchoolRoomMapping() {
Map<String, String> mapping = new HashMap<String, String>();
mapping.put("k", "school");
mapping.put("v", "room");
return mapping;
}
I was going for an answer with some annotation (JsonTypeInfo and JsonUnwrapped), but those two don't work well together apparently (see this issue). That would of handled both the serialization and deserialization part of your problem, without relying on custom de/serializer. Instead, you'll need a custom deserializer, which does something along those line:
class PairDeserializer extends JsonDeserializer<Pair>{
static Map<String, Class> MAPPINGS = new HashMap<String, Class>();
#Override
public Pair deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Object key = deserializeField(jp);
Object value = deserializeField(jp);
Pair pair = new Pair();
pair.k = key;
pair.v = value;
jp.nextToken();
return pair;
}
private Object deserializeField(JsonParser jp) throws IOException, JsonParseException, JsonProcessingException {
jp.nextValue();
String className = jp.getCurrentName();
return jp.readValueAs(MAPPINGS.get(className));
}
}
Then you only need to register the mappings you need