I am trying to create a map from all the attributes that a class have.My class looks like :
public class MyInventory
{
private int tiers = 80;
private int stearing =135;
private int battery = 46;
}
Now I have collected all the methods that the class has as :
Field[] fields = this.getClass().getDeclaredFields();
Now , I am trying to create a Map out of it where keys are the values of the fields and the values are the name of the fields. Example :
Map<46,battery> ...etc
Is there a way to do it?
The attribute values for the above mentioned class were generated by mapping to properties file and by using spring annotation #ConfigurationProperties. Now I need to create the Map but keys the values of the attributes. I tried to use reflect. However did not find a way to get the value of the fields.
Thanks
You can use Introspector class.
public Map<Object, String> populateMap(final Object o) throws Exception {
Map<Object, String> result = new HashMap<>();
for (PropertyDescriptor pd : Introspector.getBeanInfo(o.getClass()).getPropertyDescriptors()) {
String fieldName = pd.getName();
if("class".equals(fieldName) continue;
Object value = pd.getReadMethod().invoke(o);
result.put(value, fieldName);
}
return result;
}
You can call the above method, passing your class as argument.
MyInventory mi = new MyInventory();
// Sets the properties of mi
mi.setXXX...
// Populates map
populateMap(mi);
Map<Integer, String> map() throws IllegalArgumentException, IllegalAccessException {
Field[] fields = getClass().getDeclaredFields();
Map<Integer,String> map = new HashMap<>();
for (Field field : fields) {
map.put(field.getInt(this), field.getName());
}
return map;
}
Of course it will not map properly if different fields have the same value.
I think, you can have getter method in your class
public class MyInventory
{
private int tiers = 80;
private int stearing =135;
private int battery = 46;
public int getBattery()
{
return battery;
}
//and other getter
}
and then you can populate your map as
map.put(inventory.getBattery(),"battery");
Because, when you have value, which means you know what is the type for which you are populating map.
You can use json parser. For example jackson:
import com.fasterxml.jackson.databind.ObjectMapper;
...
ObjectMapper mapper = new ObjectMapper();
return mapper.readValue(mapper.writeValueAsString(fooOject), HashMap.class);
Related
I want to copy fields from a complex object- that is an object which contains other objects.
Now it copies wrapper classes with no issue but how do i copy the fields and values of the subclass
code
public Map<String, Object> getValueMapFromInsuranceVehicle(Long insuranceId) throws InvocationTargetException, IllegalAccessException {
InsurancePolicy insurance = repository.findById(insuranceId).get();
Method[] methods = insurance.getInsuranceVehicle().getClass().getMethods();
Map<String, Object> map = new HashMap<String, Object>();
for (Method m : methods) {
if (m.getName().startsWith("get")) {
Object value = m.invoke(insurance.getInsuranceVehicle());
map.put(m.getName().substring(3), value);
}
}
// add other fields specific to our needs like currentYear
return map;
}
From the code above it copies insuranceVehicle fields correctly, But i would like to copy the whole InsurancePolicy object and put the values in a map.
when i try it with InsurancePolicy i get exception cannot convert InsurancaCalculation into String,
my Insurance policy object looks like this
class InsurancePolicy {
#OneToOne
private Person person;
#OneToOne
private Vehicle vehicle;
#OneToOne
private InsurancePolicyStatus status;
private LocalDate policyStart = LocalDate.now().plusDays(1);
private LocalDate policyEnd = policyStart.plusYears(1).minusDays(1);
private boolean policy_AC = true;
private boolean policy_OC = true;
private boolean policy_ASS;
private boolean policy_NNW;
private String vehicleUsageType;
InsuranceCalculation calculation
#Embedded
private InsuranceVehicle insuranceVehicle;
#Embedded
private InsuranceCustomer customer;
private String coownerHowMany;
private String abroad;
}
Finally my question how can i improve my method getValueMapFromInsuranceVehicle() to get more fields copied ?
basically how to make this code below to work
public Map<String, Object> getValueMapFromInsuranceVehicle(Long insuranceId) throws InvocationTargetException, IllegalAccessException {
InsurancePolicy insurance = repository.findById(insuranceId).get();
Method[] methods = insurance.getClass().getMethods(); // insurance instead of vehicle
Map<String, Object> map = new HashMap<String, Object>();
for (Method m : methods) {
if (m.getName().startsWith("get")) {
Object value = m.invoke(insurance); // insurance instead of insurancevehicle
map.put(m.getName().substring(3), value);
}
}
// add other fields specific to our needs like currentYear
return map;
}
To get the methods of other objects in InsurancePolicy you could use your same code but add some checks for if the object is InsuranceCustomer, InsuranceVehicle, or InsuranceCalculation by using instanceOf and if it is use the same code just with Method[] methods = insurance.getClass().getMethods(); changed to the objects class. I would recommend separating your code into more methods so you can use recursion.
In Java say I have a class that represents http headers:
public class Headers {
String 'x-requested-by' = "foo";
String 'content-type' = "application/json"
}
because of the field names with non-standard variable names (hyphens), typically a more dynamic map is used like so:
Map<String, String> map = new HashMap<String, String>();
but my quesetion is - is there a way to declare which fields will exist in there statically, instead of only at runtime?
You can use an enum map, which will only accept keys of a specified enum type, while the enum itself will statically limit options.
enum Headers {
X_REQUESTED_BY("x-requested-by"), CONTENT_TYPE("content-type");
private String headerName;
private Headers(String n) {
this.headerName = n;
}
public String getHeaderName() {
return headerName;
}
}
And use the enum map to store values:
Map<Headers, String> headerValues = new EnumMap<>(Headers.class);
Your API can then be extended with such methods as addHeader(Headers h), which makes it possible to statically limit options while keeping it type-safe.
No. Only one thing you can do is init a Map with default values after initialization.
public class Header {
public static final String X_REQUESTED_BY = "x-requested-by";
public static final String CONTENT_TYPE = "content-type";
private final Map<String, String> map = new HashMap<String, String>();
{
map.put(X_REQUESTED_BY, "foo");
map.put(CONTENT_TYPE, "application/json");
}
}
I'm looking to deserialize any unknown fields in a JSON object as entries in a Map which is a member of a POJO.
For example, here is the JSON:
{
"knownField" : 5,
"unknownField1" : "926f7c2f-1ae2-426b-9f36-4ba042334b68",
"unknownField2" : "ed51e59d-a551-4cdc-be69-7d337162b691"
}
Here is the POJO:
class MyObject {
int knownField;
Map<String, UUID> unknownFields;
// getters/setters whatever
}
Is there a way to configure this with Jackson? If not, is there an effective way to write a StdDeserializer to do it (assume the values in unknownFields can be a more complex but well known consistent type)?
There is a feature and an annotation exactly fitting this purpose.
I tested and it works with UUIDs like in your example:
class MyUUIDClass {
public int knownField;
Map<String, UUID> unknownFields = new HashMap<>();
// Capture all other fields that Jackson do not match other members
#JsonAnyGetter
public Map<String, UUID> otherFields() {
return unknownFields;
}
#JsonAnySetter
public void setOtherField(String name, UUID value) {
unknownFields.put(name, value);
}
}
And it would work like this:
MyUUIDClass deserialized = objectMapper.readValue("{" +
"\"knownField\": 1," +
"\"foo\": \"9cfc64e0-9fed-492e-a7a1-ed2350debd95\"" +
"}", MyUUIDClass.class);
Also more common types like Strings work:
class MyClass {
public int knownField;
Map<String, String> unknownFields = new HashMap<>();
// Capture all other fields that Jackson do not match other members
#JsonAnyGetter
public Map<String, String> otherFields() {
return unknownFields;
}
#JsonAnySetter
public void setOtherField(String name, String value) {
unknownFields.put(name, value);
}
}
(I found this feature in this blog post first).
If I have class like this:
class MyObject {
public int myInt;
public String myString;
}
Is it possible to convert instance of this class to HashMap without implementing converting code?
MyObject obj = new MyObject();
obj.myInt = 1; obj.myString = "string";
HashMap<String, Object> hs = convert(obj);
hs.getInt("myInt"); // returns 1
hs.getString("myString"); // returns "string"
Does Java provide that kind of solution, or I need to implement convert by myself?
My Class has more than 50 fields and writing converter for each field is not so good idea.
With jackson library this is also possible
MyObject obj = new MyObject();
obj.myInt = 1;
obj.myString = "1";
ObjectMapper mapObject = new ObjectMapper();
Map < String, Object > mapObj = mapObject.convertValue(obj, Map.class);
You can use reflection for implementing this behavior. You can get all fields of the class you want to convert to map iterate over this fields and take the name of each field as key of the map. This will result in a map from String to object.
Map<String, Object> myObjectAsDict = new HashMap<>();
Field[] allFields = SomeClass.class.getDeclaredFields();
for (Field field : allFields) {
Class<?> targetType = field.getType();
Object objectValue = targetType.newInstance();
Object value = field.get(objectValue);
myObjectAsDict.put(field.getName(), value);
}
}
Something like that will do the trick:
MyObject obj = new MyObject();
obj.myInt = 1; obj.myString = "string";
Map<String, Object> map = new HashMap<>();
// Use MyObject.class.getFields() instead of getDeclaredFields()
// If you are interested in public fields only
for (Field field : MyObject.class.getDeclaredFields()) {
// Skip this if you intend to access to public fields only
if (!field.isAccessible()) {
field.setAccessible(true);
}
map.put(field.getName(), field.get(obj));
}
System.out.println(map);
Output:
{myString=string, myInt=1}
You might consider using a map instead of a class.
Or have your class extend a map such as
public class MyObject extends HashMap<String, Object> {
}
If you don't want to use Reflection then you can use my trick. hope this may help for someone.
Suppose your class looks like this.
public class MyClass {
private int id;
private String name;
}
Now Override toString() method in this class. in Eclipse there is a shortcut for generating this method also.
public class MyClass {
private int id;
private String name;
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("MyClass [id=").append(id).append(", name=").append(name).append("]");
return builder.toString();
}
}
Now write a method inside this class that will convert your object into Map<String,String>
public Map<String, String> asMap() {
Map<String, String> map = new HashMap<String, String>();
String stringRepresentation = this.toString();
if (stringRepresentation == null || stringRepresentation.trim().equals("")) {
return map;
}
if (stringRepresentation.contains("[")) {
int index = stringRepresentation.indexOf("[");
stringRepresentation = stringRepresentation.substring(index + 1, stringRepresentation.length());
}
if (stringRepresentation.endsWith("]")) {
stringRepresentation = stringRepresentation.substring(0, stringRepresentation.length() - 1);
}
String[] commaSeprated = stringRepresentation.split(",");
for (int i = 0; i < commaSeprated.length; i++) {
String keyEqualsValue = commaSeprated[i];
keyEqualsValue = keyEqualsValue.trim();
if (keyEqualsValue.equals("") || !keyEqualsValue.contains("=")) {
continue;
}
String[] keyValue = keyEqualsValue.split("=", 2);
if (keyValue.length > 1) {
map.put(keyValue[0].trim(), keyValue[1].trim());
}
}
return map;
}
Now from any where in your application you can simply call this method to get your HashMap from the Object. Cheers
Updated approach using reflection:
public static <T> Map<String, String> parseInnerClass(T classInstance) {
LinkedHashMap<String, String> ret = new LinkedHashMap<>();
for (Field attr : classInstance.getClass().getDeclaredFields()) {
String attrValue = "";
attr.setAccessible(true);
try {
attrValue = attr.get(classInstance).toString();
} catch (IllegalAccessException | NullPointerException e) {
// Do not add nothing
}
ret.put(attr.getName(), attrValue);
}
return ret;
}
I want to set the values of georgiaHash and marylandHash into Key_journal_model class. That model class only contains these 2 hashmaps and their getter setter methods.
I tried with object but thats not working.
demoHash is a method which returns hashmap.
public class Hash_function {
public ArrayList<Key_journal_model> setHashmaps() {
ArrayList<Key_journal_model> kjmList = new ArrayList<Key_journal_model>();
HashMap<String, Float> georgiaHash = demoHash();
HashMap<String, Float> marylandHash = demoHash();
Key_journal_model kjm = new Key_journal_model(georgiaHash,marylandHash);
kjmList.add(kjm);
return kjmList;
}