We have this Json:
{
"id": 500,
"field1": "TESTE",
"banco": {
"id": 300,
"descricao": "BANCO_TESTE"
},
"categorias": [
{
"id": 300,
"descricao": "PT",
"publica": true
}
]
}
And my beans:
public class Asking implements Serializable {
private long id;
private String field1;
private Bank bank;
private List<Categoria> categorias;
//[getters and setters]
}
The beans Bank and Categoria:
public class Bank implements Serializable {
private Long code;
private Long id;
private String descricao;
//getters and setters
}
public class Categoria implements Serializable {
private Long code;
private Long id;
private String descricao;
private boolean marcada;
private boolean publica;
//getters and setters
}
When I call:
gson.fromJson(strJson, tokenType);
The error appears:
Method threw 'java.lang.StackOverflowError' exception.
What is wrong?
I can't reproduce this problem. One of two things are wrong here:
Your beans are not defined as you say they are. Check to see if they have other fields hidden within the getter and setter method section. This can happen if you have a circular reference.
You've stated in the comments that this is likely to be your problem. I recommend:
Remove the extra fields from your bean
Create a new class that contains the extra fields, and a field for the Asking instance
Deserialize the Asking instance using Gson, and then pass it into the new class's constructor.
You are doing something unexpected with your setup of the gson.fromJson method. Here's what I'm using that works great:
public static void parseJSON(String jsonString) {
Gson gsonParser = new Gson();
Type collectionType = new TypeToken<Asking>(){}.getType();
Asking gsonResponse = gsonParser.fromJson(jsonString, collectionType);
System.out.println(gsonResponse);
}
Either check your bean class definitions for extra fields, or, failing that, try to make your deserialization match mine.
Related
The difficulty is, that the json labels are partly dynamic in the rest response of the Kraken api. At first I introduce a working case. I connect the Kraken trading api to fetch currencies as assets and got the following result in json.
{
"error": [],
"result": {
"AAVE": {
"aclass": "currency",
"altname": "AAVE",
"decimals": 10,
"display_decimals": 5
},
"ZUSD": {
"aclass": "currency",
"altname": "USD",
"decimals": 4,
"display_decimals": 2
}
}
}
AAVA and ZUSD in this example are dynamic labels. I use the embedded Jackson to parse it in the OpenFeign framework. The result part are covered with the following generic class:
public class Response<T> {
private List<String> error = new ArrayList<>();
private T result;
// getter and setters
}
And as root class for the assets, the dynamic labels AAVA and ZUSD are handled by a Map:
public class AssetInfoResponse extends
Response<Map<String, AssetInfo>> {
}
The pojo AssetInfo:
public class AssetInfo implements Serializable{
#JsonProperty("altname")
private String alternateName;
#JsonProperty("aclass")
private String assetClass;
#JsonProperty("decimals")
private Byte decimals;
#JsonProperty("display_decimals")
private Byte displayDecimals;
// getters, setters ...
}
The above case works perfectly, also the solution with the dynamic labels.
Here is the response with the ohlc data, that looks similar and I have no Idea to solve the deserialization problem in the next case:
{
"error": [],
"result": {
"XXBTZEUR": [
[
1613212500,
"39000.1",
"39010.1",
"38972.3",
"38994.1",
"38998.1",
"3.23811638",
70
],
[
1613212560,
"38994.3",
"39014.5",
"38994.3",
"39014.5",
"38997.3",
"0.95105956",
11
]
],
"last": 1613212500
}
}
The cause of the problem is the "last": 1613212500 property line. When I remove this line, the response can be parsed without problems. I try to solve it with the following classes, Response is the upper described class.
public class OhlcLastResponse<T> extends Response<T> {
private Long last;
// getters and setters
}
The next class extends the prevourious class and is the root class for the objectmapper:
public class OhlcResponse
extends OhlcLastResponse<Map<String, List<Candelstick>>> {
}
And the pojo that holds the candlestick data:
#JsonFormat(shape = JsonFormat.Shape.ARRAY)
#JsonPropertyOrder({ "time", "open", "high", "low",
"close", "vwap", "volume", "count" })
public class Candelstick implements Serializable {
private Integer time;
private BigDecimal open;
private BigDecimal high;
private BigDecimal low;
private BigDecimal close;
private BigDecimal vwap;
private BigDecimal volume;
private Integer count;
// getters and setters ...
}
and here is the error:
"38997.3",
"0.95105956",
11
]
],
"last": 1613212500
}
}
"; line: 26, column: 11] (through reference chain: OhlcResponse["result"]->java.util.LinkedHashMap["last"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59)
Jackson tries to put the last property into the map, but the map was finished by ],. Line 26 is the line with the
last label in the json file.
Is there a possibility to parse this json? I think it must be possible, because the array is closed by the square bracket.
I hosted the rest client on github. To reproduce the error just clone it and run mvn test.
The solution is a deserializer, because the type handling is very difficult in this case. The deserializer decide between the two cases, array or the single last value, and call in the case of an array the deserializer for the CandleStick Class:
public class OhlcDeserializer extends JsonDeserializer<OhclPayload> {
#Override
public OhclPayload deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
List<CandleStick> candleSticks = new ArrayList<CandleStick>();
Long last = null;
ObjectCodec objectCodec = p.getCodec();
JsonNode jsonNode = objectCodec.readTree(p);
Iterator<Entry<String, JsonNode>> payloadIterator = jsonNode.fields();
while (payloadIterator.hasNext()) {
Map.Entry<String, JsonNode> entry = payloadIterator.next();
if (entry.getKey().equals("last")) {
last = entry.getValue().asLong();
} else if (entry.getValue().isArray()) {
for (JsonNode node : entry.getValue()) {
CandleStick cs = p.getCodec().treeToValue(node, CandleStick.class);
candleSticks.add(cs);
}
}
}
return new OhclPayload(candleSticks, last);
}
}
I changed the OhclResponse to:
public class OhlcResponse extends Response<OhclPayload> {
}
And insert a OhlcPayload class for the deserializer:
#JsonDeserialize(using = OhlcDeserializer.class)
public class OhclPayload {
private List<CandleStick> candleSticks;
private Long last;
// getters and setters
}
Thats all.
I'm trying to deserialize some JSON to a generic class. The structure is roughly as follows:
public abstract class AbstractRequest implements Constants
{
public abstract Class<?> getClazz();
}
public class GetTransaction extends AbstractTransactionRequest
{
#Override
public Class<Transaction> getClazz()
{
return Transaction.class;
}
}
And the Transaction class is as follows:
public class Transaction implements Serializable
{
#SerializedName("_id")
private String id;
private int amount;
#SerializedName("details")
private Map<String, String> transactionDetails;
private class Details {
private String issuer;
#SerializedName("redirect_url")
private String redirectUrl;
#SerializedName("approval_url")
private String approvalUrl;
}
}
All classes are slightly more complicated but I removed irrelevant variables.
Here's a JSON sample:
{
"_id": "2740096e-58a0-4677-8947-84fcc54cfaad",
"amount": 456,
"details": {
"issuer": "MYBANK",
"redirect_url": "https://example.com/redirect/MYBANK",
"approval_url": "https://example.com/v1/transaction/2740096e-58a0-4677-8947-84fcc54cfaad/MYBANK/authorize"
}
}
Now, I deserialize this code by doing
response.setData(Gson.fromJson(this.getResponse(), this.request.getClazz()));
Where setData accepts a Object, and getResponse returns the JSON as a String. I then do (Transaction) response.getData() which casts data to a Transaction. However, this is always null. Can anyone tell my why?
Sorry for the potentially confusing code!
may I know how to ignore some of member variables from the class that declared it. For example, below is the 3 classes, which is PersonalInfo and declared by AcedemicInfo and FamilyInfo.
public class PersonalInfo {
#JsonPropetry
private String name;
#JsonPropetry
String universityName;
#JsonPropetry
private String motherName;
#JsonPropetry
private String fatherName;
/* Set and Get*/
}
public class AcademicInfo {
#JsonPropetry
private PersonalInfo info; // need name and university only
/* Set and Get*/
}
public class FamilyInfo {
#JsonPropetry
private PersonalInfo info; // need name and fatherName and motherName only
/* Set and Get*/
}
However, I need to ignore some of the member variables of PersonalInfo as the AcedemicInfo and FamilyInfo does not need all the attribute from PersonalInfo.
Below is my desired output
// Acedemic info json
{
"info" : {
"name":"Adam",
"universityName":"University"
}
}
// Family info json
{
"info" : {
"name":"Adam",
"fatherName":"Matt"
"motherName":"Jane"
}
}
I know about #JsonIgnore, but if I put the annotation in the PersonalInfo class, the variable will be ignore by all the class that declare it, which is not what I want. May I know how to ignore the variable conditionally? Sorry for my bad English.
One way to do is use #JsonFilter with SimpleFilterProvider and SimpleBeanPropertyFilter to exclude properties not to serialize.
You class will look like follows:
public class AcademicInfo {
#JsonFilter("academicPersonalInfoFilter")
private PersonalInfo info;
}
and the example to serialize the object:
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("academicPersonalInfoFilter",
SimpleBeanPropertyFilter.serializeAllExcept("motherName", "fatherName"));
ObjectMapper mapper = new ObjectMapper();
mapper.setFilters(filterProvider);
mapper.writeValueAsString(academicInfo);
Another way to do is use multiple #JsonView to define a set of properties to serialize.
In you case, you can define views like follows:
public class Views {
public static class BasicPersonalInfo {
}
public static class AcademicPersonalInfo extends BasicPersonalInfo {
}
public static class FamilyPersonalInfo extends BasicPersonalInfo {
}
}
and annotate the field to be serialized in the corresponding view:
public class PersonalInfo {
#JsonView(Views.BasicPersonalInfo.class)
private String name;
#JsonView(Views.AcademicPersonalInfo.class)
String universityName;
#JsonView(Views.FamilyPersonalInfo.class)
private String motherName;
#JsonView(Views.FamilyPersonalInfo.class)
private String fatherName;
}
and serialize object as follows:
String result = mapper.writerWithView(Views.AcademicPersonalInfo.class)
.writeValueAsString(academicInfo);
I want to use Gson to Deserialize my JSON into objects.
I've defined the appropriate classes, and some of those class' objects are included in other objects.
When trying to deserialize the whole JSON, I got null values, so I started breaking it apart.
I reached the point where all lower classes stand by them selves, but when trying to deserialize into an object that holds an instance of that smaller object - every thing returns as null.
My partial JSON:
{
"user_profile": {
"pk": 1,
"model": "vcb.userprofile",
"fields": {
"photo": "images/users/Screen_Shot_2013-03-18_at_5.24.13_PM.png",
"facebook_url": "https://google.com/facebook",
"site_name": "simple food",
"user": {
"pk": 1,
"model": "auth.user",
"fields": {
"first_name": "blue",
"last_name": "bla"
}
},
"site_url": "https://google.com/"
}
}
}
UserProfile Class:
public class UserProfile {
private int pk;
private String model;
private UPfields fields = new UPfields();//i tried with and without the "new"
}
UPfields Class:
public class UPfields {
private String photo;
private String facebook_url;
private String site_name;
private User user;
private String site_url;
}
User Class:
public class User {
private int pk;
private String model;
private Ufields fields;
}
Ufields Class:
public class Ufields {
private String first_name;
private String last_name;
}
In my main I call:
Gson gson = new Gson();
UserProfile temp = gson.fromJson(json, UserProfile.class);
So my temp object contain only null values.
I've tried changing the classes to static, and it doesn't work.
The UPfields object and all lower one work fine.
Any suggestions?
when I remove the
"{
"user_profile":"
and it's closing bracket, the deserialize to a user_profile object works.
In order to parse this json example you have to create auxiliary class, which will contain field named user_profile of type UserProfile:
public class UserProfileWrapper {
private UserProfile user_profile;
}
and parse this json string with this class:
UserProfileWrapper temp = gson.fromJson(json, UserProfileWrapper.class);
Gson starts by parsing the outermost object, which in your case has a single field, user_profile. Your UserProfile class doesn't have a user_profile field, so it can't deserialize it as an instance of that class. You should try to deserialize the value of the user_profile field instead.
suppose I've got a collection of people defined like this in JSON.
{
"NOM": "Doe",
"PRENOM": "John",
"EMAIL": "john.doe#email.me",
"VILLE": "Somewhere",
"LIKE1": "Lolcats",
"LIKE2": "Loldogs",
"LIKE3": "Lolwut",
"HATE1": "Bad stuff",
"HATE2": "Bad bad stuff"
}
Is it possible to write a JsonDeserializer that will aggregate and transform LIKE* and HATE* fields into a collection of Liking, set as a property of Person? (Note that there are only LIKE1, LIKE2, LIKE3, HATE1, HATE2.)
The final result properties would be something like:
public class Person {
private final String lastName;
private final String firstName;
private final String email;
private final String town;
private final Collection<Liking> likings;
// c-tor, getters
}
I've already the logic that can deserialize a given LIKE*/HATE* property into a Liking object but I fail to understand to aggregate and add them to a Person liking attribute.
Thx in advance!
It would have been nice if you had some code that showed you began the process of solving this problem yourself. But, here is a sample custom deserializer that does pretty much what you're looking for:
class PersonDeserializer extends JsonDeserializer<Person> {
#Override
public Person deserialize(final JsonParser parser,
final DeserializationContext content) throws IOException,
JsonProcessingException {
final ObjectCodec codec = parser.getCodec();
final JsonNode node = codec.readTree(parser);
final Person person = new Person();
final Iterator<String> fieldNameIter = node.getFieldNames();
while (fieldNameIter.hasNext()) {
final String fieldName = fieldNameIter.next();
if (fieldName.equalsIgnoreCase("EMAIL")) {
person.setEmail(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("NOM")) {
person.setFirstName(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("PRENOM")) {
person.setLastName(node.get(fieldName).getTextValue());
} else if (fieldName.equalsIgnoreCase("VILLE")) {
person.setTown(node.get(fieldName).getTextValue());
} else if (fieldName.startsWith("LIKE")) {
person.addLike(Liking.LikingType.LIKE, node.get(fieldName)
.getTextValue());
} else if (fieldName.startsWith("HATE")) {
person.addLike(Liking.LikingType.HATE, node.get(fieldName)
.getTextValue());
}
}
return person;
}
}
It presumes a Liking object similar to this:
public class Liking {
public static enum LikingType {
LIKE, HATE;
}
private LikingType type;
private String value;
// Constructors, getters/setters
}
And some changes to your Person object which I think you can figure out. If you intend to serialize the object to JSON in the same custom format then you will have to write a corresponding JsonSerializer.
Another option, not quite as robust, would be too simply use a map to store the likes and dislikes exactly as is. This solution would omit any explicit mappings for likes/dislikes and utilize the #JsonAny annotation to capture them. In this scheme the Person object would look like this:
public class Person {
private String lastName;
private String firstName;
private String email;
private String town;
#JsonAny
private Map<String, Object> otherProperties;
// Constructors, getters/setters
}
Deserializing your JSON into this modified version of Person will place all unrecognized properties into the hash map, as key-value pairs.
I'm pretty sure you can't do it the way you intend, how about doing it like this:
{
"NOM": "Doe",
"PRENOM": "John",
"EMAIL": "john.doe#email.me",
"VILLE": "Somewhere",
"likings": ["Lolcats", "Loldogs", "LIKE3": "Lolwut", "Bad stuff", "Bad bad stuff" ]
}