GSON returns StackOverflowError: null - java

I'm trying to transform my HashMap into a JSON string using Google's lib. GSON.
I've tried using Jackson's ObjectMapper but it didn't work either. I've tried to put some transient and some things I found on other topics without success.
It is currently returning an error (without any line specified):
//ERROR
java.lang.StackOverflowError: null
at sun.reflect.misc.ReflectUtil.checkPackageAccess(Unknown Source) ~[?:1.8.0_201]
at sun.reflect.misc.ReflectUtil.checkPackageAccess(Unknown Source) ~[?:1.8.0_201]
at sun.reflect.generics.reflectiveObjects.TypeVariableImpl.getGenericDeclaration(Unknown Source) ~[?:1.8.0_201]
at com.google.gson.internal.$Gson$Types.declaringClassOf($Gson$Types.java:427) ~[$Gson$Types.class:?]
at com.google.gson.internal.$Gson$Types.resolveTypeVariable($Gson$Types.java:397) ~[$Gson$Types.class:?]
at com.google.gson.internal.$Gson$Types.resolve($Gson$Types.java:329) ~[$Gson$Types.class:?]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.getBoundFields(ReflectiveTypeAdapterFactory.java:158) ~[ReflectiveTypeAdapterFactory.class:?]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.create(ReflectiveTypeAdapterFactory.java:100) ~[ReflectiveTypeAdapterFactory.class:?]
at com.google.gson.Gson.getAdapter(Gson.java:423) ~[Gson.class:?]
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory.createBoundField(ReflectiveTypeAdapterFactory.java:115) ~[ReflectiveTypeAdapterFactory.class:?]
// HashMap I need in JSON
public class Storage {
static #Getter #Setter HashMap<World, HashMap<Chunk, List<Particle>>> storage = new HashMap<>();
}
// Class to load/save the data file
public class StorageManager {
public static File folder, config;
private static final Gson GSON = new Gson();
public static void setup(File _folder){
folder = _folder;
}
public static void load() throws IOException {
if(!folder.exists())
folder.mkdir();
config = new File(folder, "particles.json");
if(!config.exists()) {
config.createNewFile();
FileWriter writer = new FileWriter(config, false);
writer.write(GSON.toJson(Storage.getStorage()));
writer.close();
}
FileReader content = new FileReader(config);
Type type = new TypeToken<HashMap<World, HashMap<Chunk, List<Particle>>>>(){}.getType();
Storage.setStorage(GSON.fromJson(content, type));
}
public static void save() throws IOException {
String json = GSON.toJson(Storage.getStorage());
FileWriter writer = new FileWriter(config, false);
writer.write(json);
writer.close();
}
}
I need to be able to load the file, get the JSON and replace my currently empty Hashmap with the data from the file. I also need to save the file with the new Hashmap data.

Related

How to modify content of json file?

is there any option to modify content of json file? I have 2 methods, export to json file list of different objects, and import from file.
public static void exportShapeListToJson(List<Shape> list, String path) throws IOException {
ObjectMapper mapper = new ObjectMapper();
File file = new File(path);
mapper.enableDefaultTyping();
mapper.writeValue(new File(file.getName()), list);
}
public static List<Shape> importFromJson(String path) throws IOException {
File file = new File(path);
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
return mapper.readValue(file, ArrayList.class);
}
Saved data in file looks like:
["Project1.shapeService.models.Circle",{"radius":3.0}],["Project1.shapeService.models.Circle",{"radius":5.0}],["Project1.shapeService.models.Rectangle",{"a":1.0,"b":2.0}]
Can i modify that data to form like that:
[{"type": "squre", "a": 10}, {"type": "circle", "r": 10}]
and still use method to import? What have to add/ change?

Gson throws IllegalStateException when attempting to parse List

I am learning data persistence and this is my first attempt at JSON. I have read a few guides and from what little I can tell the code has been correct in both attempts at storing the objects. I get the file written using Gson but Gson throws exceptions when attempted to parse the objects using the fromJson() method. My question is as follows:
If I am using the same type to convert to/from JSON what am I missing that would tell Gson how to properly parse my object(s)?
I have tried three different approaches, two of which are included below. First I tried storing the wrapper class for the list of objects which a guide suggested I should be able to do:
public class JSONConverter {
private static Path path = Paths.get("src\\json\\JSONList.json");
private static Type stockType = new TypeToken<StocksList>(){}.getType();
public static void convertToJSON(StocksList stocks, Path path) {
Gson json = new Gson();
String storedStocks = json.toJson(stocks, stockType);// I also tried "StocksList.class" here
checkForFile(path);
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
writer.write(storedStocks);
} catch (IOException e) {
e.printStackTrace();
//handle later
}
}
static void checkForFile(Path path) {
if (Files.notExists(path)) {
try {
Files.createFile(path);
} catch (IOException e) {
e.printStackTrace();
//handle later
}
}
}
public static StocksList convertFromJSON(Path path) {
StocksList stocksList = new StocksList();
Gson json = new Gson();
String fromJson;
try {
fromJson = Files.readAllBytes(path).toString();
stocksList = json.fromJson(fromJson, stockType);
return stocksList;
} catch (IOException e) {
return stocksList;
}
}
}
My second approach was to get the list out of the wrapper class and try to convert that to JSON:
public class JSONConverter {
private static Path path = Paths.get("src\\json\\JSONList.json");
private static Type listType = new TypeToken<List<Stock>>(){}.getType();
public static void convertToJSON(StocksList stocks, Path path) {
Gson json = new Gson();
List<Stock> temp = stocks.getStocks();
String storedStocks = json.toJson(temp, listType);
checkForFile(path);
try (BufferedWriter writer = Files.newBufferedWriter(path)) {
writer.write(storedStocks);
} catch (IOException e) {
e.printStackTrace();
//handle later
}
}
static void checkForFile(Path path) {
if (Files.notExists(path)) {
try {
Files.createFile(path);
} catch (IOException e) {
e.printStackTrace();
//handle later
}
}
}
public static StocksList convertFromJSON(Path path) {
StocksList stocksList = new StocksList();
List<Stock> stocks = new ArrayList<>();
Gson json = new Gson();
String fromJson;
try {
fromJson = Files.readAllBytes(path).toString();
stocks = json.fromJson(fromJson, listType);
//wraps the list in the stockslist class
stocksList.setStocks(stocks);
return stocksList;
} catch (IOException e) {
e.printStackTrace();
return stocksList;
}
}
}
Here is a sample of the JSON written by the first method using the second approach. The first looks like it except adds "{ "stocks" :" (what you see below) "}":
[
{
"ticker": "INTC",
"currentPrice": "45.94",
"marginOfSafety": 0.25,
"lastDate": "2019-12-28",
"cashYield": "7.4",
"MCap": "196485365760",
"enterpriseValue": "281213850000",
"sharesOut": "4417000000",
"oddPercentGrowth": false,
"newCompany": false,
"safeValue": "51.35",
"fairValue": "68.47",
"evEbitda": "8.56",
"fcf": [
"16932000000",
"14611750000"
],
"rOnAssets": "21",
"rOnCapital": "20",
"croic": "16.47",
"equityToDebt": "3.0",
"cashOnHand": "4194000000",
"cashToDebt": "0.17",
"changeInDebt": "210000000",
"capEfficiency": [
"18",
"7",
"-26",
"-21",
"1"
],
"fcfChange": [
"18.81",
"11.71"
],
"profitMargin": [
"46",
"38"
]
},
{
"ticker": "HCC",
"currentPrice": "12.99",
"marginOfSafety": 0.5,
"lastDate": "2018-12-31",
"cashYield": "46.1",
"MCap": "664587904",
"enterpriseValue": "1572623480",
"sharesOut": "52812000",
"oddPercentGrowth": true,
"newCompany": true,
"safeValue": "236.94",
"fairValue": "473.87",
"evEbitda": "2.59",
"fcf": [
"457776000",
"306126750"
],
"rOnAssets": "49",
"rOnCapital": "59",
"croic": "38.77",
"equityToDebt": "1.0",
"cashOnHand": "205577000",
"cashToDebt": "0.44",
"changeInDebt": "125283000",
"capEfficiency": [
"292",
"798",
"-365",
"-397",
"-1"
],
"fcfChange": [
"33.9",
"33.9"
],
"profitMargin": [
"40",
"8"
]
}
]
Both throw:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException:
Expected BEGIN_OBJECT but was STRING at line 1 column 12
(this line changes to "Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2" when using the first approach).
at
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:176)
...
I was going to try adding each object individually to a JSONArray but when I started encountering trouble there I thought I should just ask. The guide mentioned that reflection was important and I am guessing that my trouble lies therein due to the second line of the stack trace but again, this is my first time trying to use JSON. If I forgot to include anything let me know and I'll post it in a comment.
Thanks for the help.
ADDENDUM: the objects only throw these exceptions when being written to and pulled from a file. They do not throw when being converted to a JSON String and then back again. It occurs whether I use Files.write() or Files.newBufferedWriter().
Thanks to all those who viewed my question. I reached out to Gson's github page and they responded with the following corrections to my class:
All the code you've provided can be greatly fixed, improved and refactored.
No need to create multiple Gson instances: they are relatively
expensive to instantiate, but are designed to be thread-safe and
immutable therefore can be reused. No need to serialize to and
deserialize from java.lang.String -- this is just expensive as long as
it has to create multiple strings in the heap merely wasting the heap
and time decreasing the performance. Why it does not work in your case
is that Files.readAllBytes(...) returns byte[] you're trying to
convert to a string. In Java, no arrays have an intuitive toString
implementation (you can check it by simply printing any byte array to
System.out). In order to convert it to a string (that might be a
memory-consuming instance), new String(byte[]) (or even new
String(byte[], Charset)) is an appropriate way. I don't really
remember how Files works, but there's probably no need to check the
file to exist: they can be overwritten without any additional checks.
No type tokens are necessary in this case: StockList.class is a Type
too. Essentially, all is you need is just as follows:
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.disableInnerClassSerialization()
.create();
public static void main(final String... args)
throws IOException {
final StocksList before = new StocksList(ImmutableList.of(new Stock("INTC"), new
Stock("HCC")));
final Path path = Paths.get("doc.json");
write(path, before);
final StocksList after = read(path);
System.out.println(after.equals(before));
}
private static void write(final Path path, final StocksList stocks)
throws IOException {
try ( final Writer writer = new OutputStreamWriter(new
FileOutputStream(path.toFile())) ) {
gson.toJson(stocks, writer);
}
}
private static StocksList read(final Path path)
throws IOException {
try ( final Reader reader = new InputStreamReader(new
FileInputStream(path.toFile())) ) {
return gson.fromJson(reader, StocksList.class);
}
}
Thanks to lyubomyr-shaydariv (Gson contributor) for the answer.

Avro Map Serialization/Deserialization issue

Currently I am using Avro 1.8.0 to serialize / deserialize objects but facing issue especially for java.util.Map object. Not facing issue with other type of objects.
Sample code here -
class AvroUtils {
public byte[] serialize(Object payload) {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
Schema schema = new ReflectDatumWriter().getData().induce(payload); //---> getting proper map schema as {"type":"map","values":"string"}
JsonEncoder jsonEncoder = EncoderFactory.get().jsonEncoder(schema, out);
final GenericDatumWriter<Object> writer = new GenericDatumWriter(schema);
writer.write(payload, jsonEncoder);
jsonEncoder.flush();
return out.toByteArray();
}
public <R> R deserialize(Object o, Class<R> aClass) {
Schema schema = new ReflectDatumWriter().getData().induce(o); //------> getting error - unable to get schema
final ByteArrayInputStream bin = new ByteArrayInputStream((byte[]) o);
JsonDecoder jsonDecoder = DecoderFactory.get().jsonDecoder(schema, bin);
final GenericDatumReader<R> reader = new GenericDatumReader<>(schema);
return reader.read(null, jsonDecoder);
}
public static void main(String[] args) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("Key1", "Value1");
map.put("Key2", "Value2");
// Serialization
byte[] result = this.serialize(map);
System.out.println("Serialized Data : " + new String(mapDes, "UTF-8"));
// Deserialization
Map<String, Object> mapDes = (Map<String, Object>) this.deserialize(result, Map.class);
System.out.println("Deserialized Data : " + mapDes);
}
}
In deserialize method I am trying to get the schema based on input data but avro is throwing error -
`Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to java.util.Collection
at org.apache.avro.reflect.ReflectData.getArrayAsCollection(ReflectData.java:196)
at org.apache.avro.generic.GenericData.induce(GenericData.java:612)`
Note: At the end both the methods will be placed in different libraries (avro-serializer / avro-deserializer).
Please suggest the best way to get schema in deserialization method.
Thanks.
You get java.lang.ClassCastException: [B cannot be cast to java.util.Collection cause you try to call induce() method with object which is byte array but not a Map.
If you want to serialize a map in one place and deserialize it in the other, you can use a better way:
Schema schema = Schema.createMap(Schema.create(Schema.Type.STRING));
If you do so, you will not need any additional parameters in desirialize method.
Also, GenericDatumWriter can be used only with generic records, so you need a ReflectDatumWriter.
Here is an example of your code with changes:
public class AvroUtils {
public static byte[] serialize(Object payload) throws IOException {
final ByteArrayOutputStream out = new ByteArrayOutputStream();
Schema schema = Schema.createMap(Schema.create(Schema.Type.STRING)); //---> getting proper map schema as {"type":"map","values":"string"}
JsonEncoder jsonEncoder = EncoderFactory.get().jsonEncoder(schema, out);
final DatumWriter<Object> writer = new ReflectDatumWriter<>(schema);
writer.write(payload, jsonEncoder);
jsonEncoder.flush();
return out.toByteArray();
}
public static <R> R deserialize(Object o) throws IOException {
Schema schema = Schema.createMap(Schema.create(Schema.Type.STRING));
JsonDecoder jsonDecoder = DecoderFactory.get().jsonDecoder(schema, new ByteArrayInputStream((byte[]) o));
final DatumReader<R> reader = new ReflectDatumReader<>(schema);
return reader.read(null, jsonDecoder);
}
public static void main(String[] args) throws IOException {
Map<String, Object> map = new HashMap<>();
map.put("Key1", "Value1");
map.put("Key2", "Value2");
// Serialization
byte[] result = serialize(map);
// Deserialization
Map<String, Object> mapDes = deserialize(result);
System.out.println("Deserialized Data : " + mapDes);
}
}
As a result, you'll get something like this:
Deserialized Data : {Key2=Value2, Key1=Value1}

Importing json that was serialized by JSON is failing

So I have an object with some fields...
protected String name;
protected String relativePathAndFileName;
protected DateTime next_Run;
protected ArrayList<String> hosts;
Which gets serialized to JSON like this:
public void serialize(){
Gson gson = Converters.registerDateTime(new GsonBuilder()).setPrettyPrinting().create();
String json = gson.toJson(this);
try {
FileWriter writer = new FileWriter(this.relativePathAndFileName);
writer.write (json);
writer.close();
} catch (IOException e) {
logger.error("Error while trying to write myAlert to json: ", e);
}
}
Later when I need to read in this json file, I try to do so like this:
try {
for (File f : alertConfigFiles) {
Gson gson = new Gson();
JsonReader reader = new JsonReader(new FileReader(f));
Type type = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> myMap = gson.fromJson(reader, type);
Alert tempAlert = new Alert(myMap);
myAlerts.add(tempAlert);
logger.debug("Imported: " + f.toString());
}
The error that I'm getting is:
Unhandled exception when trying to import config files:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY at line 28 column 13 path $.
The JSON inside the file is something to the effect of:
{
"name": "Logs Per Host - past 24 hours",
"relativePathAndFileName": "./Elk-Reporting/Alerts/logs_per_host24h.json",
"next_Run": "2017-06-07T22:24:56.682-04:00",
"hosts": [
"app-12c",
"app1-18",
"wp-01",
"app-02",
"wp-02",
"cent-04",
"app-06",
"app-05"
]
}
It seems to be choking when it tries to import the ArrayList of hosts, but it was able to write them out without issues.
Can anyone offer some advice on how to get my import working without issues?
try to keep it simple. Using maps and so on, is a way to have issues.
Here is a working code to deserialise / serialise :
package com.rizze.beans.labs.sof;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import com.google.gson.Gson;
public class SOFGson {
public String json = "{ \"name\": \"Logs Per Host - past 24 hours\", \"relativePathAndFileName\": \"./Elk-Reporting/Alerts/logs_per_host24h.json\", \"next_Run\": \"2017-06-07T22:24:56.682-04:00\", \"hosts\": [ \"bos-qa-app-12c\", \"bos-qa-app1-18\", \"bos-qa-wp-01\", \"bos-lt-app-02\", \"bos-qa-wp-02\", \"bos-dev-cent-04.americanwell.com\", \"bos-qa-app-06\", \"bos-qa-app-05\" ]}";
public class MyObj{
protected String name;
protected String relativePathAndFileName;
protected String next_Run;
protected String[] hosts;
}
#Test
public void test() {
Gson gson = new Gson();
MyObj obj = gson.fromJson(json, MyObj.class);
assertTrue(obj!=null);
assertTrue(obj.hosts.length==8);
System.out.println(gson.toJson(obj));
}
}
here is the class in gist : https://gist.github.com/jeorfevre/7b32a96d4ddc4af68e40bf95f63f2c26
Those two lines seem to be the problem:
Type type = new TypeToken<Map<String, String>>(){}.getType();
Map<String, String> myMap = gson.fromJson(reader, type);
You serialize your object of some specific class. You then deserialize it to type. But your JSON does not fit into a Map. Better do it like this, so you can use your own class.
YourClass myMap = gson.fromJson(reader, YourClass.class);
If you want to use this approach, you might want to change your Java class to hold an array of strings instead of an ArrayList of strings.
Maybe this page helps you a bit. Especially the first case fits your situation.
Another option is a custom Deserialzer as described here.

Java REST service. Info is not saved to file and my code doesnt read the file

I am creating my first Rest service using JSON objects for the data
transfer between user and server, with the help of the Gson library 2.5.
I am not using any frameworks like Jersey or anything like that. (That was my
project requirment). The java version i use is 1.6 (part of my requirment)
jboss server and Eclipse as IDE.
At the moment i have 2 small functions from a simple HTML form. The first is
suposed to requests the data from the JSON file and the second is suposed to
add a new json information to the json document.
Problem is: When i try to acces the JSON file, a array its returned with the
last submited Person. When i save a new Person information, that information is
not saved in the personsJsonFile but someplace else [have no ideea where].
My json file is found in the Projects main folder.
Any help is deeply apreciated.
GetData class:
#Path("/data")
public class GetDataClass {
#GET
#Produces("text/plain")
public ArrayList<PersonConstructor> displayJsonFile() throws IOException{
ArrayList<PersonConstructor> newLib = new ArrayList<PersonConstructor>();
File jsonFile = new File("personsJsonFile.json");
Scanner fileInput = new Scanner(jsonFile);
Gson gson = new Gson();
while(fileInput.hasNextLine()){
String jsonLine = fileInput.nextLine();
PersonConstructor singlePerson = gson.fromJson(jsonLine, PersonConstructor.class);
newLib.add(singlePerson);
}
fileInput.close();
return newLib;
}
}
AddData Class:
#Path("/add")
public class AddPersonsClass {
#POST
public String addPersons(
#FormParam("idInput") int idInput,
#FormParam("surnameInput") String surnameInput,
#FormParam("nameInput") String nameInput
) throws IOException
{
Gson gson = new Gson();
PersonConstructor newPerson = new PersonConstructor();
newPerson.setPersonId(idInput);
newPerson.setPersonNume(nameInput);
newPerson.setPersonPrenume(surnameInput);
File jsonFile = new File("personsJsonFile.json");
FileWriter jsonWriter = new FileWriter(jsonFile);
System.out.println(newPerson);
String jsonLine = gson.toJson(newPerson);
System.out.println(newPerson);
jsonWriter.write(jsonLine+"\n");
jsonWriter.close();
return "Element: " + newPerson + "has been added";
}
}
PersonConstructor Class:
public class PersonConstructor {
private int personId;
private String personNume;
private String personPrenume;
public PersonConstructor(int personId, String personNume,String personPrenume){
this.personId = personId;
this.personPrenume = personPrenume;
this.personNume = personNume;
}
public PersonConstructor() {
}
public int getPersonId(){
return personId;
}
public void setPersonId(int personId){
this.personId = personId;
}
public String getPersonNume(){
return personNume;
}
public void setPersonNume(String personNume){
this.personNume = personNume;
}
public String getPersonPrenume(){
return personPrenume;
}
public void setPersonPrenume(String personPrenume){
this.personPrenume = personPrenume;
}
public String toString(){
return String.format("\n%s %s %s\n", this.personId, this.personNume, this.personPrenume);
}
}
Json file contains:
{"personId":5,"personNume":"Ursu","personPrenume":"Niculae"},
{"personId":6,"personNume":"Ivan","personPrenume":"Claudiu"},
{"personId":7,"personNume":"Hap","personPrenume":"Dorel"}
Your problem seems to that you have not specified the path where to save the file.
Add the path when creating a file.
final String jsonDirectory = "path to file";
File file = new File(jsonDirectory + "\\results.txt");

Categories