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
Related
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
I'm trying to use the Java Jackson ObjectMapper to parse a three level JSON object stucture with dynamic keys. I tried the following:
public class AssetsPushManifest {
private Map<String, List<Asset>> manifest = new HashMap<>();
public void addPushManifest(Resource manifestResource) throws Exception {
ObjectMapper mapper = new ObjectMapper();
Map<String, Map<String, Asset>> manifestData = mapper.readValue(manifestResource.getInputStream(), new TypeReference<Map<String, Map<String, Asset>>>() {});
for (String requestedPathName : manifestData.keySet()) {
if (!this.manifest.containsKey(requestedPathName)) {
this.manifest.put(requestedPathName, new LinkedList());
}
List<Asset> requestedPath = this.manifest.get(requestedPathName);
for (String servePath : manifestData.get(requestedPathName).keySet()) {
Asset asset = manifestData.get(requestedPathName).get(servePath);
asset.path = servePath;
requestedPath.add(asset);
}
}
...
}
public class Asset {
public String path;
public String type;
public Integer weight;
}
}
To parse this:
{
"theme/test-theme/index.html": {
"theme/test-theme/somestyling.css": {
"type": "document",
"weight": 1
}
}
}
But it won't work, why oh why? Is it too many levels? (still Java beginner here)
The end goal is to parse the several JSON structures like above into a structure like Map> so any other ways of doing this would also be fine.
I would solve this in a different way: parse the json into a map: if you give Jackson a map as type reference, it will deserialize the JSON into multi-level map:
`Map<String, Object> manifestData = mapper.readValue(manifestResource.getInputStream(), Map.class);`
Now that the json parsing hurdle is behind us, it is easier to construct an instance of an Asset by querying the map. I would do it by adding a constructor to the Asset class:
public Asset(Map<String, Object> manifestData) {
Map<String, Object> assetData = (Map<String, Object>)manifestData.values().iterator().next(); // get the single value of the map
this.path = assetData.keySet().iterator().next();
this.type = assetData.get("type");
this.weight = assetData.get("weight");
}
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 was wondering if it possible to convert Statics Classes with Jackson instead Doing a parser by myself. For Example I have the next Static Class:
public static class SQL {
public enum Table {
CREATE,
ALTER
}
public enum Database {
CREATE
}
}
And I want to convert to next Json String
{
"SQL" :
{
"Table":
{"CREATE": "CREATE", "ALTER": "ALTER"},
{"CREATE": "CREATE"}
}
}
This is because I can use this class in Java to create great strings, and I want a similar functionality in Javascript Side.
I tried to use
new ObjectMapper.writeValuesToString(SQL);
However, this is impossible since it only converts an instantiated Object
You can achieve something like you want by using Reflection. You can add simply method to your SQL class, which can return Map.
public static Map<String, ?> toJsonMap() {
Map<String, Object> map = new HashMap<String, Object>();
Class<?>[] declaredClasses = SQL.class.getDeclaredClasses();
for (Class<?> clazz : declaredClasses) {
if (clazz.isEnum()) {
map.put(clazz.getSimpleName(), clazz.getEnumConstants());
} else {
// ...
}
}
return Collections.singletonMap(SQL.class.getSimpleName(), map);
}
Simple usage:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(SQL.toJsonMap()));
Above program prints:
{
"SQL" : {
"Database" : [ "CREATE" ],
"Table" : [ "CREATE", "ALTER" ]
}
}