I'm trying to parse a json file that looks like this
{
"foo": "v2",
"bar": [
"abc/bcf<object#twenty>.xyz",
"abc/fgh<object#thirtu>.xyz"
]
}
The code I have is currently this:
Config.java
private static final ObjectMapper OBJECT_MAPPER;
static {
OBJECT_MAPPER = new ObjectMapper();
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
OBJECT_MAPPER.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
OBJECT_MAPPER.enableDefaultTyping();
}
#JsonCreator
public Config(
#JsonProperty(value = "foo", required = true) final String version,
#JsonProperty(value = "bar") final List<String> barTypes) {
// rest of constructor
}
public static Config fromJson(final Reader reader)
throws IOException {
return OBJECT_MAPPER.readValue(reader, Config.class);
}
I am getting an error:
Failed to parse type 'abc/bcf<object#twenty>.xyz' (remaining: '<object#twenty>.xyz'): Cannot locate class 'abc/bcf', problem: abc/bcf"
Is there something special I need to do in order to read "<" as String?
I'm reading the file into a BufferReader with StandardCharsets.UTF_8 like this:
try (BufferedReader reader = Files.newBufferedReader(configFile.toPath(), StandardCharsets.UTF_8)) {
config = Config.fromJson(reader);
}
Edit: I actually do need defaultTyping for an ArrayList that has polymorphic types:
"Vehicles": [
"java.util.ArrayList",
[
{
"name": "Car"
},
{
"name": "Train"
}
]
I currently use a MixIn for declaring the subtypes. However, this stops working if I remove DefaultTyping.
Remove OBJECT_MAPPER.enableDefaultTyping(); and it will work fine. This method is anyway deprecated.
In case you want to read automatic polymorphic types, use activateDefaultTyping(PolymorphicTypeValidator ptv).
Related
I have two JSON files, called "pickevent1" and "pickevent2". I have to compare if both files are matching; if they don't match, I need to know where they don't match.
pickevent1
{
"pickEventActivities": [{
"orderId": "215",
"lineNbr": 0,
"pick": "EACH",
"activations": [{
"activationType": "Si",
"activationValue": "31"
}]
}]
}
pickevent2
{
"pickEventActivities": [{
"orderId": "115",
"lineNbr": 1,
"pick": "Hello",
"activations": [{
"activationType": "Bi",
"activationValue": "3"
}]
}]
}
I created a pick event POJO class:
#JsonRootName(value = "pickEventActivities")
#Data
#JsonPropertyOrder({ "orderId", "lineNbr", "pick"})
class PickEvent {
String orderId;
String lineNbr;
String pick;
List<Activation> activations;
}
and a Activation POJO class:
#Data
#JsonPropertyOrder({ "activationType", "activationValue"})
public class Activation {
String activationType;
String activationValue;
}
To make sure it works, I created a test class:
public void compareJson() throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
PickEvent result1 = objectMapper.readValue(new File("src/../pickevent1.json"), PickEvent.class);
PickEvent result2 = objectMapper.readValue(new File("src/../pickevent2.json"), PickEvent.class);
assertEquals(result1, result2);
}
But when I am doing assertSame(result1,result2) its giving me null for json values:
Exception in thread "main" java.lang.AssertionError: expected same:<PickEvent(orderId=null, lineNbr=null, pick=null, activations=null)> was not:<PickEvent(orderId=null, lineNbr=null, pick=null, activations=null)>
at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotSame(Assert.java:828)
at org.junit.Assert.assertSame(Assert.java:771)
at org.junit.Assert.assertSame(Assert.java:782)
at JsonDiff.PickEventDiff.comparejson(PickEventDiff.java:26)
at JsonDiff.PickEventDiff.main(PickEventDiff.java:32)
It should give me an assertion error, but the test succeeds.
It should give me an assertion error, but the test succeeds.
Because you use objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);. In fact, an exception occurred during the parsing process.
Try:
public void compareJson() throws Exception {
final ObjectMapper objectMapper = new ObjectMapper();
Wrapper wrapper = objectMapper.readValue(new File(""), Wrapper.class);
Wrapper wrapper2 = objectMapper.readValue(new File(""), Wrapper.class);
System.out.println(wrapper.equals(wrapper2));
}
#Data
static class Wrapper {
List<PickEvent> pickEventActivities;
}
You are trying to read a PickEvent Object but you're actually sending a list there.
Please change your json to
{
"pickEventActivities": {
"orderId": "115",
"lineNbr": 1,
"pick": "Hello",
"activations": [{
"activationType": "Bi",
"activationValue": "3"
}]
}
}
or try changing your code to
List<PickEvent> list1 = objectMapper.readValue(new File("src/../pickevent1.json"), new ParameterizedTypeReference<PickEvent>(){});
Here is my github repo for Intellij Idea plugin.
Basically JSON comparator implemented in java. It compares JSON by fields, values and objects in array
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.
Following is Response body:
{
"Updated_Fields": [
"a",
"b",
"c",
"d"
],
"Invalid_Fields": [
"cd",
"ab"
]
}
I want to check that whether response body has
two fields in invalid_field block
'cd' and 'ab' should be there in invalid_field block
JSONArray JSONResponseBody = new JSONArray(response.body().asString());
Assert.assertEquals(JSONResponseBody.getJSONObject(0).getString("Invalid_Fields"), "cd");
response.jsonPath().param("Invalid_Fields", "cd");
assertThat( response.asString(), hasJsonPath("Invalid_Fields.ab"));
Getting an error
One way you can use a library like gson to convert String to Java object and then apply standard Java logic ( sample below )
Gson Maven Dependency
private static final List INVALID_DATA = Arrays.asList("cd", "ab");
public static void main(String[] args)
{
String input = "{ \"Updated_Fields\": [ \"a\", \"b\", \"c\", \"d\" ], \"Invalid_Fields\": [ \"cd\", \"ab\" ] }";
Gson gson = new Gson();
FieldData data = gson.fromJson(input, FieldData.class);
System.out.println(isInvalidFields(data.Invalid_Fields));
}
private static boolean isInvalidFields(List<String> Invalid_Fields) {
if(CollectionUtils.isEmpty(Invalid_Fields) || Invalid_Fields.size() != 2) {
return false;
}
return Invalid_Fields.containsAll(INVALID_DATA);
}
Definiton of class mapping to this data:
public class FieldData
{
public List<String> Updated_Fields;
public List<String> Invalid_Fields;
}
I have a Json file :
[
{
"name":"Move",
"$$hashKey":"object:79",
"time":11.32818,
"endTime":18.615535
},
{
"name":"First Red Flash",
"$$hashKey":"object:77",
"time":15.749153
},
{
"name":"Pills",
"subEventTypes":[
"pull down bottle",
"unscrew lid",
"dump pills out",
"screw lid on",
"put bottle away"
],
"$$hashKey":"object:82",
"time":25.130175,
"subEventSplits":[
26.092057,
27.425881,
31.841594,
34.268093
],
"endTime":36.234827
}
]
I tried to parse this Json file using the Jackson.
I wrote the following code:
public class Holder{
public Holder(){};
//getter and setters
String name;
List<String> subEventTypes = new ArrayList<>();
Double time;
String $$hashKey;
Double endTime;
List<Double> subEventSplits = new ArrayList<>();
}
class MapperClass{
List<Holder> list = new ArrayList<>();
}
public static void main(String[] args) throws JsonProcessingException, IOException
{
ObjectMapper mapper = new ObjectMapper();
List<Holder> list = mapper.readValue(new File("data.json"), mapper.getTypeFactory().constructCollectionType(
List.class, Holder.class));
}
When I run the program, it showed this error : "
No suitable constructor found for type [simple type, class parseJason$Holder]: can not instantiate from JSON object (need to add/enable type information?)
".
Is there anything wrong with my code? or I have to use another way to parse my Json file.
try
list = mapper.readValue(
jsonString,
objectMapper.getTypeFactory().constructCollectionType(
List.class, Holder.class));
I want to make a post request with volley to a REST API.
Therefore, I create a JSONObject and put a JSON String generated from a class in it.
JSONObject jsonObject = new JSONObject();
String json = gson.toJson(MyClazz);
try {
jsonObject.put(PARAM, json);
}
catch (JSONException e1) {
e1.printStackTrace();
}
The problem is that the correct calculated JSON String gets escaped and can't be recognized on the back end.
So toJson() gives something like:
{
"device_identifier":"324234234",
"name":"NameMe",
"list":[
{"prop":"A","prop2":-10},
{"prop":"B","prop2":-12}
]
}
The jsonObject's output is like
{
"PARAM":{
\"device_identifier\":\"324234234\",
\"name\":\"NameMe\",
\"list\":[
{\"prop\":\"A\",\"prop2\":-10},
{\"prop\":\"B\","\prop2\":-12}
]
}
}
I need the PARAM for the JSON structure so I can't give it directly to the REST-API. Any ideas how I can avoid the additional escaping?
You could wrap your MyClazz object with a simple wrapper object, and then pass that wrapped object to Gson's toJson method.
Given this class based on your example JSON,
public class MyClazz {
public String device_identifier;
public String name;
public List<Prop> list;
public class Prop {
public String prop;
public Integer prop2;
}
}
here's a possible wrapper implementation. Note the use of com.google.gson.annotations.SerializedName which tells Gson to use the PARAM key in the JSON representation.
public class MyClazzWrapper {
public MyClazzWrapper(MyClazz myClazz) {
this.myClazz = myClazz;
}
#SerializedName("PARAM")
private MyClazz myClazz;
}
And here's an example using it:
Gson gson = new GsonBuilder().setPrettyPrinting().create();
MyClazz myClazz = gson.fromJson("{\"device_identifier\":\"324234234\",\"name\":\"NameMe\",\"list\":[{\"prop\":\"A\",\"prop2\":-10},{\"prop\":\"B\",\"prop2\":-12}]}", MyClazz.class);
MyClazzWrapper wrapped = new MyClazzWrapper(myClazz);
System.out.println(gson.toJson(wrapped));
The above will print:
{
"PARAM": {
"device_identifier": "324234234",
"name": "NameMe",
"list": [
{
"prop": "A",
"prop2": -10
},
{
"prop": "B",
"prop2": -12
}
]
}
}