I have the following class
public class Params {
private String dataType;
private Map<String, List<String>> group;
private List<Aggregate> aggregates;
private List<String> scene;
private List<String> company;
private List<Map<String, List<String>>> fit;
private Map<String, String> params;
private String tempo;
private String id;
private String valuation;
}
In order to convert it to a tsv, I have serialized it to json using:
public String put(final Params parameter) {
serializedParams = JsonSerializer.serialize(params);
}
So I get something like this as an output:
{
"dataType" : "abc",
"group" : { },
"aggregates" : [ ],
"scene" : [ "AC" ],
"company" : [ "pr" ],
"fit" : [ {
"prod" : [ "A1" ]
} ],
"params" : null,
"tempo" : "note",
"id" : "123",
"valuation" : USD
}
My eventual output is getting something like this (tab-separated):
abc AC pr prod A1 note 123 USD
I tried using :
parameter.getGroup.values()
This, however, only gives the values present in a format like [ val ].
How can I get all the values of the object simultaneously? Is it possible to get values from a map, list etc simultaneously without separate processing?
Any help would be appreciated.
It's not quite clear from your question exactly how you want to handle your data when generating your tabbed output for certain scenarios, but I'll try to provide some sample code to help you.
First, however, I want to say that it's not a good idea to serialize to JSON and then serialize JSON to tab separated values. You're doing 2 steps when you could be doing one, the JSON serialization doesn't get you any closer to your goal, and serialization is generally a costly process - you don't want to do it more than you have to.
So with that in mind here's a quick example of how you might implement a simple tab delimited serialization:
Since you have at least one custom object to deal with inside of your Params class (referring to the List of Aggregateobjects) I would suggest creating an interface that your classes can implement to indicate they are able to be serialized into a tab delimited string.
I simply called this interface TabSerializable:
public interface TabSerializable {
public String toTabbedString();
}
Next, I modified your Params class to implement this interface:
public class Params implements TabSerializable{
//fields omitted for brevity.
...
public String toTabbedString(){
StringJoiner joiner = new StringJoiner("\t");
TabSerializer.addValue(dataType, joiner);
TabSerializer.addValue(group, joiner);
TabSerializer.addValue(aggregates, joiner);
TabSerializer.addValue(scene, joiner);
TabSerializer.addValue(company, joiner);
TabSerializer.addValue(fit, joiner);
TabSerializer.addValue(params, joiner);
TabSerializer.addValue(tempo, joiner);
TabSerializer.addValue(id, joiner);
TabSerializer.addValue(valuation, joiner);
return joiner.toString();
}
...
}
I also created a sample Aggregate class which also implements this interface:
public class Aggregate implements TabSerializable{
private String data;
#Override
public String toTabbedString() {
return data;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
Since you have mostly generic types of data structures like maps and lists I created the TabSerializer class to help transform those structures into tab delimited strings:
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
public class TabSerializer {
private static void addValuesFromMap(Map<?,?> obj, StringJoiner joiner) {
for(Object key: obj.keySet()){
Object value = obj.get(key);
if(value == null){
continue;
}
addValue(key, joiner);
addValue(value, joiner);
}
}
private static void addValuesFromList(List<?> arr, StringJoiner joiner) {
for(int i=0; i<arr.size(); i++){
Object value = arr.get(i);
addValue(value, joiner);
}
}
public static void addValue(Object value, StringJoiner joiner){
if(value == null){
return;
}
if(value instanceof List){
addValuesFromList((List<?>)value, joiner);
}else if(value instanceof Map){
addValuesFromMap((Map<?,?>)value, joiner);
}else if(value instanceof TabSerializable){
joiner.add(((TabSerializable) value).toTabbedString());
}else{
joiner.add(String.valueOf(value));
}
}
}
Based on the example you gave in your question I wrote the logic above to skip null values and the names of fields at the top level (those fields in the Params class).
Finally I created a class with a main to test the above logic:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MainTabDelim {
public static void main(String[] args) {
Params params = new Params();
params.setDataType("abc");
List<String> scene = new ArrayList<>();
scene.add("AC");
params.setScene(scene);
List<String> company = new ArrayList<>();
company.add("pr");
params.setCompany(company);
List<Map<String, List<String>>> fit = new ArrayList<>();
List<String> fitInner = new ArrayList<>();
fitInner.add("A1");
Map<String, List<String>> fitMap = new HashMap<>();
fitMap.put("prod", fitInner);
fit.add(fitMap);
params.setFit(fit);
params.setTempo("note");
params.setId("123");
params.setValuation("USD");
// Uncomment the following lines to see how Aggregate works:
// List<Aggregate> aggregates = new ArrayList<>();
// Aggregate ag = new Aggregate();
// ag.setData("some_data");
// aggregates.add(ag);
// params.setAggregates(aggregates);
System.out.println(params.toTabbedString());
}
}
The output is:
abc AC pr prod A1 note 123 USD
I hope this helps you to get started. Enjoy!
Same CSV generate code you have to use, and only add a few things when you create the TSV file as below
try (CSVWriter writer = new CSVWriter(new FileWriter("src/main/resources/test2.tsv"),
'\t',
CSVWriter.NO_QUOTE_CHARACTER,
CSVWriter.DEFAULT_ESCAPE_CHARACTER,
CSVWriter.DEFAULT_LINE_END)) {
writer.writeAll(dataLines);
}
I used OpenCsV maven library
Related
I have a simple class in java like below:
class Simple {
private String name;
private String email;
}
I want to have behaviour of java.util.List<Simple> and Simple both according to input data that my program receives.
i.e.
Case 1::
if my program receives below kind of json-array input
{"simple" : [ {"name":"a", "email" : "a#z.com"}, {"name":"b", "email" : "b#z.com"} ]}
I need to parse it using List<Simple>
Case 2::
if my program receives below kind of json-object input
{"simple" : {"name":"c", "email" : "c#z.com"} }
I need to parse it using Simple
Note: I have tried using JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, but the problem is it is basically converting single value also into json-array at the time of writing json.
I need to persist json as it is, is there any other way to achieve this?
To avoid any Jackson customisation I would create wrapper class with an Object simple property. We can add two extra checking methods and two extra casting methods. It will allow Jackson to do it's logic and in runtime we can check what actually we have:
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Collections;
import java.util.List;
public class DateApp {
private final static JsonMapper JSON_MAPPER = JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build();
public static void main(String[] args) throws Exception {
Simple object = new Simple("John", "john#doe.com");
SimpleWrapper wrapper = new SimpleWrapper();
wrapper.setSimple(object);
serializeAndDeserialize(wrapper);
wrapper.setSimple(Collections.singletonList(object));
serializeAndDeserialize(wrapper);
}
private static void serializeAndDeserialize(SimpleWrapper wrapper) throws JsonProcessingException {
String json = JSON_MAPPER.writeValueAsString(wrapper);
System.out.println("JSON:");
System.out.println(json);
wrapper = JSON_MAPPER.readValue(json, SimpleWrapper.class);
System.out.println("Wrapper:");
System.out.println(wrapper);
}
}
#Data
class SimpleWrapper {
private Object simple;
#JsonIgnore
public boolean isSimpleObject() {
return simple instanceof Simple;
}
#JsonIgnore
public boolean isSimpleList() {
return simple instanceof List;
}
#JsonIgnore
public Simple getSimpleAsObject() {
return (Simple) simple;
}
#JsonIgnore
public List<Simple> getSimpleAsList() {
return (List<Simple>) simple;
}
}
#Data
#NoArgsConstructor
#AllArgsConstructor
class Simple {
private String name;
private String email;
}
Above code prints:
JSON:
{
"simple" : {
"name" : "John",
"email" : "john#doe.com"
}
}
Wrapper:
SimpleWrapper(simple={name=John, email=john#doe.com})
JSON:
{
"simple" : [ {
"name" : "John",
"email" : "john#doe.com"
} ]
}
Wrapper:
SimpleWrapper(simple=[{name=John, email=john#doe.com}])
You can use JsonNode.isArray() (or JsonNode.isObject()) to perform this check.
Then you can parse the node into a list with ObjectReader.readValue() or into a POJO using ObjectMapper.treeToValue().
String myJson = """
{"simple" : {"name":"c", "email" : "c#z.com"} }
""";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(myJson);
if (node.isArray()) {
ObjectReader reader = mapper.readerFor(new TypeReference<List<Simple>>() {});
List<Simple> list = reader.readValue(node);
// do something with a list
} else {
Simple pojo = mapper.treeToValue(node, Simple.class);
// do something else with a single object
}
Jackson is able to parse any json into a map where value is any object. you can then inquire on the type of the map value
Map<String, Object> map = new ObjectMapper().readValue(jsonInput, Map.class);
Object value = map.get("simple");
if (value instanceof Collection) { // will return false for null
Collection<Simple> simples = (Collection<Simple>)value;
}
else if (value instanceof Simple) {
Simple simple = (Simple)value;
}
else {
System.err.println("unrecognized");
}
You only need to read the first node, simple and check if it is an array - using isArray() method.
public class Main{
public static void main(String args[]) {
//String inputString = [your input];
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(inputString);
JsonNode simpleNode = root.findPath("simple");
if(simpleNode.isArray()) {
//you have an array
} else {
// you have an element
}
}
}
My service can receive several different jsons, such as:
{
"event":"conversation_started",
"context":"context information",
"user":{
"id":"01234567890A=",
"name":"John McClane",
"avatar":"http://avatar.example.com",
"country":"UK",
"language":"en",
"api_version":1
},
"subscribed":false
}
or
{
"event":"message",
"message":{
"type":"text",
"text":"a message to the service",
"location":{
"lat":12.34,
"lon":12.34
}
}
}
or several else jsons. The only field that is the same for all jsons is "event". All other fields can be different (depends on "event" value).
So the question is: how to convert those jsons to java objects (without making messy code)? The only way I know is to manually check "event" value (like json.startsWith("{\n\"event\":\"message\"") but I'm sure that there is any simple decision for doing this.
There are three ways I've done this. The first is to do what you're suggesting - parse the JSON, check the type, and create the object. Be very careful with using a String parser as you may or may not have things like new lines. Instead, do something like:
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(eventString);
String eventType = jsonNode.get("event").asText();
if( eventType.equalsIgnoreCase("conversation_started")) {
// create ConversationStarted object using something like:
ConversationStarted conversationStarted = objectMapper.readValue( eventString, ConversationStarted.class );
}
This, of course, requires all classes to have a concrete POJO to allow for deserialization.
Another way is to do what many other programming languages do and have a key/value map. There are a few ways to do this. One is with the Jackson libraries:
Map<String, Object> map = objectMapper.readValue(eventString, new TypeReference<Map<String,Object>>(){});
Map<String, Object> user = (Map<String, Object>) map.get("user");
System.out.println( "conversation started - avatar is " + user.get("avatar"));
That way you can pass around the Map and extract as needed. Note that you still need to understand the structure of the JSON but you don't need to have a POJO for it.
Lastly is a variation on the second solution. Using JSONPath you can pull out what you need directly. Again you will want to first check out which type of event you have. Something like:
if( JsonPath.read(eventString, "$.event").equals("conversation_started") ) {
String avatar = JsonPath.read(eventString, "$.user.avatar");
System.out.println("conversation started - avatar is " + avatar);
}
The last two methods require you to pull out values one at a time as shown. The first solution gives you a full object to work with. It is your call as to what works best in your environment.
UPD: If you don't want to convert JSON String to JAVA Object via declaring a POJO, you can parse it to JSONObject(com.alibaba.fastjson.JSONObject)
public class Event {
public static void main(String[] args) {
String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";
JSONObject jsonObject = JSONObject.parseObject(jsonA);
String event = jsonObject.getString("event");
if (event.equals("message")) {
//do what you want to do
System.out.println("message event......");
} else if ("conversation_started".equals(event)) {
System.out.println("context information event......");
}
}
}
Declaring a class of Event as below, and then convert JSON String to a Event JAVA object.
#Data
public class Event {
private String event;
private String context;
private User user;
private boolean subscribed;
private Message message;
#Data
public static class User {
private String id;
private String name;
private String avatar;
private String country;
private String language;
private int api_version;
}
#Data
public static class Message {
private String type;
private String text;
private Location location;
#Data
public static class Location {
private double lat;
private double lon;
}
}
public static void main(String[] args) {
String jsonA = "{\"event\":\"conversation_started\",\"context\":\"context information\",\"user\":{\"id\":\"01234567890A=\",\"name\":\"John McClane\",\"avatar\":\"http://avatar.example.com\",\"country\":\"UK\",\"language\":\"en\",\"api_version\":1},\"subscribed\":false}";
String jsonB = "{\"event\":\"message\",\"message\":{\"type\":\"text\",\"text\":\"a message to the service\",\"location\":{\"lat\":12.34,\"lon\":12.34}}}";
ObjectMapper objectMapper = new ObjectMapper();
try {
Event eventA = objectMapper.readValue(jsonA, new TypeReference<Event>() {
});
System.out.println(objectMapper.writeValueAsString(eventA));
Event eventB = objectMapper.readValue(jsonB, new TypeReference<Event>() {
});
System.out.println(objectMapper.writeValueAsString(eventB));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Use a JSON object. This is dynamic and can load any json. Then you can reference the event field consistently
Example 1
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"event\" : \"conversation_started\", \"context\" : \"context information\", \"user\" : { \"id\" : \"01234567890A=\", \"name\" : \"John McClane\", \"avatar\" : \"http://avatar.example.com\", \"country\" : \"UK\", \"language\" : \"en\", \"api_version\" : 1 }, \"subscribed\" : false }");
System.out.println(((String)root.get("event")));
Example 2
//import java.util.ArrayList;
//import org.bson.Document;
Document root = Document.parse("{ \"event\" : \"message\", \"message\" : { \"type\" : \"text\", \"text\" : \"a message to the service\", \"location\" : { \"lat\" : 12.34, \"lon\" : 12.34 } } }");
System.out.println(((String)root.get("event")));
I have JSON file looks like
{
"SUBS_UID" : {
"featureSetName" : "SIEMENSGSMTELEPHONY MULTISIM",
"featureName" : "MULTISIMIMSI",
"featureKey" : [{
"key" : "SCKEY",
"valueType" : 0,
"value" : "0"
}
]
},
}
So the key is a String "SUBS_ID" and the value is a model called FeatureDetails which contains attributes "featureSetName,featureName,...".
So i read from the JSON file using google.json lib like this,
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, HashMap.class);
then I'm trying to loop over this HashMap getting the value and cast it to my FeatureDetails model,
for (Map.Entry entry : featuresFromJson.entrySet()) {
featureDetails = (FeatureDetails) entry.getValue();
}
and here is my FeatureDetails Model,
public class FeatureDetails {
private String featureSetName;
private String featureName;
private ArrayList<FeatureKey> featureKey;
private String groupKey;
private String groupValue;
public FeatureDetails() {
featureKey = new ArrayList<FeatureKey>();
}
public ArrayList<FeatureKey> getFeatureKey() {
return featureKey;
}
public void setFeatureKey(ArrayList<FeatureKey> featureKey) {
this.featureKey = featureKey;
}
public String getGroupKey() {
return groupKey;
}
public void setGroupKey(String groupKey) {
this.groupKey = groupKey;
}
public String getGroupValue() {
return groupValue;
}
public void setGroupValue(String groupValue) {
this.groupValue = groupValue;
}
public String getFeatureName() {
return featureName;
}
public void setFeatureName(String featureName) {
this.featureName = featureName;
}
public String getFeatureSetName() {
return featureSetName;
}
public void setFeatureSetName(String featureSetName) {
this.featureSetName = featureSetName;
}
}
but i got an exception "com.google.gson.internal.LinkedHashTreeMap cannot be cast to com.asset.vsv.models.FeatureDetail".
try this:
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, new TypeToken<Map<String, FeatureDetails>>() {}.getType());
and when you going through your hash map do this:
for (Map.Entry<String, FeatureDetails> entry : featuresFromJson.entrySet()) {
FeatureDetails featureDetails = entry.getValue();
}
The reason you're seeing this is because you're telling GSON to deserialize the JSON structure using the structure of a HashMap in the line
... = new Gson().fromJson(JSONFeatureSet, HashMap.class);
^^
Right here
As a result, GSON has no idea that the sub objects in the JSON are anything other than simple key-value pairs, even though the structure may match the structure of your FeatureDetails object.
One solution is to create a model which wraps your FeatureDetails object, which will act as the root of the entire structure. This object might look something like this:
public class FeatureDetailsRoot{
private FeatureDetails SUBS_UID; // poor naming, but must match the key in your JSON
}
And finally, you'd pass that model's class:
= new Gson().fromJson(JSONFeatureSet, FeatureDetailsRoot.class)
Update
In answer to your question in the comment regarding the ability to add / have multiple FeatureDetails objects, the problem presently is that your JSON does not reflect that kind of structure. Meaning, the "SUBS_UID" key points to a single object, not an array objects. If you would like to have this ability, then your json will need to be altered so that it shows an array of objects, like this:
{
"SUBS_UID" : [{
"featureSetName" : "Feature set name #1",
...attributes for feature #1
},
{
"featureSetName" : "Feature set name #2",
...attributes for feature #2
},
...other features
]
}
And then you can simply alter the root class so that it contains a list of FeatureDetails objects, like so:
public class FeatureDetailsRoot{
private List<FeatureDetails> SUBS_UID;
}
Let me know if that makes sense (or whether I've misunderstood you)
(objectName as Map<String, Any>).get("fieldName")
the code is in Kotlin:
use val type = object : TypeToken<HashMap<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
instead of val type = object : TypeToken<Map<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
note: HashMap instead of Map
In my Android app I have json, which looks like :
{
"Records": [
{
"RowIndex": "0",
"NameValue": {
"Name": "PropertyName1",
"Value": "PropertyValue1"
}
}{
"RowIndex": "1",
"NameValue": {
"Name": "PropertyName2",
"Value": "PropertyValue2"
}
}
]
}
I need to parce this json to object, which looks like:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
And result after parsing should be:
public String PropertyName1 = "PropertyValue1";
public String PropertyName2 = "PropertyValue2";
Basically, the first json is equivalent of:
{
"PropertyName1" : "PropertyValue1",
"PropertyName2" : "PropertyValue2"
}
Question: How can I parce first json without usage swith/case to search for the necessary Property?
You'll have to go down the dark path of reflection I'm afraid.
you can parse the json into an intermediary object which has a map for namevalue.
then you use the below code (ofcourse just copy paste the bits you need) to loop over the map of key/value pairs. for each key look up the field you want, and set it. If you're guaranteed only to need to set public variables then you can use getFields and can skip the setAccessible.
public class Test {
public static void main(String[] argv) {
MyClass myClass = new MyClass();
Class<?> classObject = myClass.getClass();
// Field fields[] = classObject.getFields(); // if you want to get only public fields.
Field fields[] = classObject.getDeclaredFields(); // any field
for(Field f : fields) {
System.out.println(f.getName());
try {
// if member is private: security managers may object but the default java allows it
f.setAccessible(true);
f.set(myClass, "abc");
} catch (IllegalAccessException e) {
// handle access exception:
e.printStackTrace();
}
}
System.out.println("prop 1: " + myClass.PropertyName1);
System.out.println("prop 2: " + myClass.PropertyName2);
}
public static class MyClass {
public String PropertyName1;
private String PropertyName2;
}
}
Actually.. there is a non-reflect way but that will replace your implementation of the object you have.
If you change your class:
public class MyClass {
public String PropertyName1;
public String PropertyName2;
}
to
public class MyClass {
private Map<String, String> properties = new HashMap<String, String>();
public void setProperties(Map<String, String> props) { this.properties = props; }
public String getPropertyName1() {
return lookupProperty("PropertyName1");
}
public String getPropertyName2() {
return lookupProperty("PropertyName2");
}
private String lookupProperty(String property) {
if (properties.containsKey(property) {
return properties.get(property);
} else {
return null;
}
}
}
then you could parse the name value map into a map, and construct a myclass with it.
just listing it for completeness, though changing your domain model to fit a json input is not ideal.
I would recommend either way to do the input parsing, and then copy over the model into your actual domain object rather than using the json-model in your application. that way if the json model ever changes, your domain model will not change.
One method I can think of (which doesn't sound too great) is to actually make an object that matches the JSON response you get back. Then, map THAT NameValue object to MyClass
So, something like
public class NameValue {
public string Name;
public String Value;
public MyClass getMyClass(){
MyClass myClass = new MyClass();
myClass.PropertyName2 = Value;
return myClass;
}
}
You can come up with a better way to map it, obviously. But this is just an example of something I might do if I was given a response JSON I didn't particularly care for. You can similarly reverse it (have MyClass be able to create a NameValue object) so you can send data back in the correct format.
I have JSON file looks like
{
"SUBS_UID" : {
"featureSetName" : "SIEMENSGSMTELEPHONY MULTISIM",
"featureName" : "MULTISIMIMSI",
"featureKey" : [{
"key" : "SCKEY",
"valueType" : 0,
"value" : "0"
}
]
},
}
So the key is a String "SUBS_ID" and the value is a model called FeatureDetails which contains attributes "featureSetName,featureName,...".
So i read from the JSON file using google.json lib like this,
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, HashMap.class);
then I'm trying to loop over this HashMap getting the value and cast it to my FeatureDetails model,
for (Map.Entry entry : featuresFromJson.entrySet()) {
featureDetails = (FeatureDetails) entry.getValue();
}
and here is my FeatureDetails Model,
public class FeatureDetails {
private String featureSetName;
private String featureName;
private ArrayList<FeatureKey> featureKey;
private String groupKey;
private String groupValue;
public FeatureDetails() {
featureKey = new ArrayList<FeatureKey>();
}
public ArrayList<FeatureKey> getFeatureKey() {
return featureKey;
}
public void setFeatureKey(ArrayList<FeatureKey> featureKey) {
this.featureKey = featureKey;
}
public String getGroupKey() {
return groupKey;
}
public void setGroupKey(String groupKey) {
this.groupKey = groupKey;
}
public String getGroupValue() {
return groupValue;
}
public void setGroupValue(String groupValue) {
this.groupValue = groupValue;
}
public String getFeatureName() {
return featureName;
}
public void setFeatureName(String featureName) {
this.featureName = featureName;
}
public String getFeatureSetName() {
return featureSetName;
}
public void setFeatureSetName(String featureSetName) {
this.featureSetName = featureSetName;
}
}
but i got an exception "com.google.gson.internal.LinkedHashTreeMap cannot be cast to com.asset.vsv.models.FeatureDetail".
try this:
HashMap<String, FeatureDetails> featuresFromJson = new Gson().fromJson(JSONFeatureSet, new TypeToken<Map<String, FeatureDetails>>() {}.getType());
and when you going through your hash map do this:
for (Map.Entry<String, FeatureDetails> entry : featuresFromJson.entrySet()) {
FeatureDetails featureDetails = entry.getValue();
}
The reason you're seeing this is because you're telling GSON to deserialize the JSON structure using the structure of a HashMap in the line
... = new Gson().fromJson(JSONFeatureSet, HashMap.class);
^^
Right here
As a result, GSON has no idea that the sub objects in the JSON are anything other than simple key-value pairs, even though the structure may match the structure of your FeatureDetails object.
One solution is to create a model which wraps your FeatureDetails object, which will act as the root of the entire structure. This object might look something like this:
public class FeatureDetailsRoot{
private FeatureDetails SUBS_UID; // poor naming, but must match the key in your JSON
}
And finally, you'd pass that model's class:
= new Gson().fromJson(JSONFeatureSet, FeatureDetailsRoot.class)
Update
In answer to your question in the comment regarding the ability to add / have multiple FeatureDetails objects, the problem presently is that your JSON does not reflect that kind of structure. Meaning, the "SUBS_UID" key points to a single object, not an array objects. If you would like to have this ability, then your json will need to be altered so that it shows an array of objects, like this:
{
"SUBS_UID" : [{
"featureSetName" : "Feature set name #1",
...attributes for feature #1
},
{
"featureSetName" : "Feature set name #2",
...attributes for feature #2
},
...other features
]
}
And then you can simply alter the root class so that it contains a list of FeatureDetails objects, like so:
public class FeatureDetailsRoot{
private List<FeatureDetails> SUBS_UID;
}
Let me know if that makes sense (or whether I've misunderstood you)
(objectName as Map<String, Any>).get("fieldName")
the code is in Kotlin:
use val type = object : TypeToken<HashMap<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
instead of val type = object : TypeToken<Map<String, FoodLogEntry>>() {}.type
Gson().fromJson(dataStr, type)
note: HashMap instead of Map