I have a JSON that looks like:
{"pfm1":{"status":true,"candid":true},
"pfm2":{"status":false,"candid":true},
"pfm3":{"status":false,"candid":true}}
I want to map it and change the property naming for pfm1,pfm2,pfm3 with a variable string name. Could you please indicate how it can be done in the class below.
The output shall be:
{"Idname01":{"status":true,"candid":true},
"Idname02":{"status":false,"candid":true},
"Idname03":{"status":false,"candid":true}}
Msg jsonobject=mapper.readValue(input, Msg.class);
Class
static class Msg {
#JsonIgnore
private Object pfm1;
#JsonIgnore
private Object pfm2;
#JsonIgnore
private Object pfm3;
private Map<String, Object> pfm = new HashMap<String, Object>();
public Object getPfm1() {return pfm1;}
public void setMpfm1(Object pfm1) {this.pfm1 = pfm1;}
public Object getPfm2() {return mpfm2;}
public void setPfm2(Object pfm2) {this.pfm2 = pfm2;}
public Object getPfm3() {return mpfm3;}
public void setPfm3(Object pfm3) {this.pfm3 = pfm3;}
#JsonAnySetter
public void set(String name, Object value) {
mpfm.put(name, value);
}
public Msg(){
}
}
Found solution for that without using getters, setters and #jsonanygetter. Just by using Object node. it is possible to manipulate your json structure including changing name property. it's much simpler and straightforward.
newNode.set("newname",node);
///convert constructed newNode to json
String jsonout = newNode.toString();
"newname" can be set as a stored variable. node is Object node extracted from top structure.
Related
I have one specific case. I need to serialize/deserialize an object to Map<String, Object>. I have a class that looks like the following:
public class Data {
public String name;
public Map<String, Object> options = new HashMap<>();
}
I can put to this options objects of any type. For instance:
public class Option {
public int id;
...
}
public class TestOpt {
public name;
...
}
and I try to serialize and deserialize it:
public static void main(String... args) {
ObjectMapper mapper = new ObjectMapper();
Option o = new Option();
o.id = 1;
TestOpt t = new TestOpt();
t.name = "fff";
Data data = new Data();
data.name = "data";
data.options.put("o", o);
data.options.put("t", t);
String json = mapper.writeValueAsString(data);
Data d1 = mapper.readValue(json, Data.class);
// I get error because options.get("o") contains LinkedHashMap instead of Option.class
System.out.println(((Option)d1.options.get("o")).id);
}
How can I fix this issue?
The value of the serialized json is
{"name":"data","options":{"t":{"name":"fff"},"o":{"id":1}}}
So, the problem is that the object mapper has no way to tell that the o value inside the json is an Option. The best guess is that it could be a map and thus it is deserialized as a LinkedHashMap.
If you are sure that the element o is an Option, you can convert the value using an object mapper:
Option option = mapper.convertValue(d1.options.get("o"), Option.class);
But please note, that this means that the value is again serialized and then deserialized using the right type information. You can do that, but it is not a good solution.
If it is possible, a better way would be to change your model from a generic map to a specific class that contains the type information:
class Data {
public String name;
public DataOptions options = new DataOptions();
}
class DataOptions {
public Option o;
public TestOpt t;
}
Serializing this model has the same json representation as the model using a map, and the model can be used to deserialize the json from your example.
I have a Switch that contains 13 case, each case executes a different sql request. I got the result in an ArrayList<HashMap<String, String>>. This result is supposed to be displayed with angular , for now i'm using this this.respTest = JSON.stringify(response); so it displays a list of "key":"value" .
My problem is since each request gets me different database fields and values ,so I want to merge some fields .
I created this class :
public class DataCollect {
private String type ;
private String entity ;
private String modPar ;
private String dateModif ;
private String numVersion ;
public DataCollect(String type, String entity, String modPar, String dateModif, String numVersion) {
this.type = type;
this.entity = entity;
this.modPar = modPar;
this.dateModif = dateModif;
this.numVersion = numVersion;
}
public DataCollect() {
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getEntity() {
return entity;
}
public void setEntity(String entity) {
this.entity = entity;
}
public String getModPar() {
return modPar;
}
public void setModPar(String modPar) {
this.modPar = modPar;
}
public String getDateModif() {
return dateModif;
}
public void setDateModif(String dateModif) {
this.dateModif = dateModif;
}
public String getNumVersion() {
return numVersion;
}
public void setNumVersion(String numVersion) {
this.numVersion = numVersion;
} }
In this class I'm supposed to affect the fields' names to the variables that I created and as a return an arraylist of hashmap with the data I extracted from the data base.
I mean I used to return for example "field-name":"value" , I want to return "type":"value","entity":"value" ..etc
I'm using springboot for the backend and angular 5 for the front.
Any help would be appreciated.
What you essentially want is a way to map keys in [each of] your hashmap to the corresponding member variable in the "DataCollect" POJO.
If there is a one to one mapping between the key present and corresponding member variable, you can expose a public constructor in "DataCollect" that takes in the hash map and constructs the corresponding object.
public DataCollect(Map<String, String> result) {
this.type = result.get("type");
this.entity = result.get("db_entity_key");
...
}
If there is no one on one mapping, you'd have to create a factory class, which takes your Map as an input and some context, and returns you the constructed DataCollect object.
Once you have the constructor or the factory class, you only need to iterate over your results list and do the needful to convert each Map into the DataCollect object.
Your controller should automatically serialise the DataCollect objects to corresponding JSON, or you can even use Jackson's ObjectMapper to achieve the same.
I want to use Jackson to convert a Java object into JSON format. I have a class which looks pretty much the following structure
public Class Event
{
String type;
String timestamp;
String hostname;
String service;
Payload payload;
}
I have the getters and setters for the above fields and also the getters/setters in Payload class.
Here is the json format, i want
{
"type":"end",
"time":"2016-08-01 11:11:11:111",
"origin":{
"hostname":"<hostname>",
"service":"<service>"
},
"version":"1.0"
"data":{ .... }
}
I can't seem to find a jackson way to get the above format, don't know how to put the whole payload object in "data" node and how to put the hostname, service in the "origin" node.
from your question, this is one approach that should showcase on how to solve it. Since you only posted 1 class, I am changing the payload to be a map. It works the same way with other classes as well.
Consider this example:
public class JacksonTest {
public static void main(String[] args) throws JsonProcessingException {
Event e = new Event();
e.type="end";
e.service="<service>";
e.hostname = "<hostname>";
e.timestamp = LocalDateTime.now().toString();
Map<String,String> payload = new HashMap<>();
payload.put("param1", "xyz");
e.payload = payload;
String writeValueAsString = new ObjectMapper().writeValueAsString(e);
System.out.println(writeValueAsString);
}
public static class Event {
#JsonProperty
String type;
#JsonProperty("time")
String timestamp;
#JsonIgnore
String hostname;
#JsonIgnore
String service;
#JsonProperty("data")
Map<String, String> payload;
#JsonProperty("origin")
Map<String,String> getOrigin() {
Map<String,String> tmp = new HashMap<>();
tmp.put("hostname", hostname);
tmp.put("service", service);
return tmp;
}
#JsonProperty("version")
private String getVersion() {
return "1.0";
}
}
}
I annotate the Event class with the necessary properties I want and the names they should have. Since you want the hostname and service to be in a nested setting and not create a new object for it (a new object would be easier as you could just have that serialised), I ignore those and instead use a getter to create the necessary structure as a map.
The output is:
{
"type":"end",
"time":"2016-08-19T16:45:18.072",
"data":{"param1":"xyz"},
"origin":{
"hostname":"<hostname>",
"service":"<service>"
},
"version":"1.0"
}
Regads,
Artur
I have class that looks like this:
public class Data<U> {
#JsonProperty("difficulties")
private U[] data;
// ... geter setter constructor
}
And I don't want to create 10 more similar classes just because I need to change only one line of code (#JsonProperty("difficulties") in this case). The property value depends on Type. Is it possible to write it in one class?
Based on response of Jackson - Modify an attribute at runtime without annotation by Michał Ziober here I was able to change default field name values
by overriding PropertyNamingStrategy:
These are my received JSON examples (simplified):
{"status": "OK","error": null,"data": {
"difficulties": [{"value":"easy"},{"value":"medium"}]
}}
{"status": "ok", "error": null, "data": {
"countries": [{"code": "AT"},{"code": "BE"}]
}}
see the difference in second line where data object contains either difficulties
or countries (or many other names based on context).
Response class based on JSON response:
public class Response<T>{
private String status;
private String error;
private Data<T> data;
// Getters Setters Constructors
}
Data class based on JSON response:
public class Data<T> {
// property name, that will be changed
#JsonProperty(DataNamingStrategy.DATA_FIELD)
private T[] data;
// Getters Setters Constructors
}
And this is Naming strategy, that changes default value to runtime specified value
public class DataNamingStrategy extends PropertyNamingStrategy{
// used by other classes (this will be default field name that should be changed)
public static final String DATA_FIELD = "variable:data";
private String fieldName;
public DataNamingStrategy(String fieldName) {
this.fieldName = fieldName;
}
// use this to change field name (format "variable":"value") not needed in my case
#Override
public String nameForField(MapperConfig<?> config, AnnotatedField field,
String defaultName) {
return (defaultName.equals(DATA_FIELD))?
fieldName :
super.nameForField(config, field, defaultName);
}
// use this to change setter method field name (JSON -> Object with format "variable":{})
#Override
public String nameForSetterMethod(MapperConfig<?> config,
AnnotatedMethod method, String defaultName) {
return (defaultName.equals(DATA_FIELD))?
fieldName :
super.nameForGetterMethod(config, method, defaultName);
}
// use this to change getter method field name (Object -> JSON with format "variable":{})
// should be same as nameForSetterMethod
#Override
public String nameForGetterMethod(MapperConfig<?> config,
AnnotatedMethod method, String defaultName) {
return nameForSetterMethod(config, method, defaultName);
}
}
And usage should look like this:
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new DataNamingStrategy(tableName));
JavaType type = mapper.getTypeFactory().constructParametricType(Response.class, dataClass);
Response<U> u = mapper.readValue(result, type);
Where result is Json as String, tableName is String that will be used in JSON instead of default value and dataClass is class for U (for example Difficulty.class).
Better usage of PropertyNamingStrategy should be Map instead of one String. But I just needed to change one particular value.
Also have a look at PropertyNamingStrategy documentation or again at Michał Ziober's answer
You can use #JsonAnyGetter annotation.
public class Data<U> {
#JsonIgnore
private U[] data;
#JsonIgnore
private String propertyName;
public Data(String propertyName) {
this.propertyName = propertyName;
}
// ... geter setter
#JsonAnyGetter
public Map<String, Object> any() {
return Collections.singletonMap(propertyName, data);
}
}
And use it like below:
Data<Difficulties> difficulties = new Data<>("difficulties");
write whatever you want instead of "difficulties" string. Set your list to Data generic class instead of Difficulties object if you want
Again I would like to get your opinion about a design issue.
I have a JavaBean with 15 attributes. For feeding the attributes I have a for loop that iterates over a collection of key-value pairs (concretely SAML attributes, I am mapping the attributes response to principals attributes). I am invoking the appropriate setter method basis on the key value, this is:
.../...
for (SAML2AttributeInfo attr : attrs) {
if (attr.getAttributeName().equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn")) {
customPrincipal.setUpn(attr.getAttributeValues().iterator().next());
}
.../... so on and so forth
}
It works, ok, but I have an ugly piece of code, 15 if-statements like above do not look very elegant.
I am thinking on using Reflection, this is, develop a unique set method and pass it the name of the attribute and his value.
Another option could be store the attributes in a Map, but I am not sure...
Any ideas?
Thanks in advance,
Luis
I would use a Map and declare static variables with the attribute keys:
public class Bean {
public static final String ATTRIBUTE_1 = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn";
public static final String ATTRIBUTE_2 = "...";
...
public static final String ATTRIBUTE_N = "...";
private Map<String, Object> map = new HashMap<String, Object>();
public void put(String key, Object value) {
map.put(key, value);
}
public Object get(String key) {
map.get(key);
}
}
Then, you coud store / retrieve values using the static variables:
Bean bean = new Bean();
bean.set(Bean.ATTRIBUTE_1, someValue);
Object value = bean.get(Bean.ATTRIBUTE_1);
Polymorphism for the rescue
enum Attribute {
UPN("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn") {
void setValue(Principal principal, String value) {
principal.setUpn(value);
}
},
...
;
private final String name;
private Attribute(String name) {
this.name = name;
}
public abstract setValue(Principal principal, String name);
public static Attribute getByName(String name) {
for (Attribute attribute : values())
if (attribute.name.equals(name))
return attribute;
return null;
}
public static void setByName(Principal principal, String name, String value) {
Attribute attribute = getByName(name);
if (attribute == null)
throw new IllegalArgumentException("No such attribute");
attribute.setValue(principal, value);
}
}
If you know the attribute you want to set there is no reason to go go via the name:
Attribute.UPN.setValue(principal, "something");