This question already has answers here:
How to convert a JSON string to a Map<String, String> with Jackson JSON
(11 answers)
Closed 4 years ago.
I have to call 3rd party API for currency exchange rate, but the JSON returned keep changing, if i request a MYR to USD conversion, it will return me: {"MYR_USD":0.246731}, so if i request for KRW to USD, it will return me {"KRW_USD":0.000888}.
I will have to map the returned result into pojo using #JsonProperty, is there any ways that works?
My current hard-coded workaround:
#JsonIgnoreProperties(ignoreUnknown = true)
public class FreeCurrencyExchangeRate {
#JsonProperty("MYR_USD")
private double rate;
public double getRate() {
return rate;
}
public void setRate(double rate) {
this.rate = rate;
}
}
{"A":5} is an object with field A having value 5.
No {"A":5} is a Map with key/value pair A=5.
Both can be true, but since the value before : is dynamic (changing), it the second interpretation you need.
So don't ask for JSON to be converted to a POJO (FreeCurrencyExchangeRate).
Ask for it to be converted to a Map, then iterate that map.
Related
I have an API which is being extended to be consumed by another system. Pre-req's exist whereby each frontend has different expectations for one json field.
Example:
The response field 'amount' must be either a String or an int, depending on which value I receive from downstream. So for some instances I will return a string value in the json, while in others I will return int.
Expected json outputs:
{
"amount": 21
}
or
{
"amount": "21"
}
I have done the following:
class Response {
#JsonProperty("amount")
private int amount;
#JsonProperty("amount")
private String amountString;
// Getters and setters
Hoping that I would be able to return either an int or String value for the json field 'amount' but I'm getting the following error:
com.fasterxml.jackson.databind.JsonMappingException: Conflicting getter definitions for property "amount"
Any help would be appreciated
You can not do that. The element in your JSON is called amount, you can not bind it to 2 different variables.
You are getting this exception because the #JsonProperty("amount") is used on 2 seperate class fields.
It is best to create 2 different response:
Example:
class FrontEndResponse1 {
#JsonProperty("amount")
private int amount;
//getter/setter
}
class FrontEndResponse2 {
#JsonProperty("amount")
private String amount;
//getter/setter
}
Yes, this won't work with typed return values.
Unless you're fine with providing 2 endpoints with different return types you could return a plain Map<String, Object> instead of Response:
public Map<String, Object> awkwardMethod(boolean wantString) {
Response response = new Response();
... // do business logic
return Map.of("amount", wantString ? String.value(response.amount) : response.amount);
}
To be clear: I'm not encouraging you to do this as you'll loose type information for the return value and introduce error prone manual mapping. I'm just providing a solution for a problem that should be fixed by providing a clear API and clients sticking to this API.
This question already has answers here:
How do I use a custom Serializer with Jackson?
(11 answers)
Closed 1 year ago.
I want to write a function that only serializes a POJO with given implicit field names.
For example,
class Car{
public int id;
public String type;
public Manufacture manufacture;
}
Class Manufacture{
public int id;
public String name;
}
if I want to serialize a Car object with a given list(i.e. [Car.id, Car.Manufacture.name])
Then I want to get
{
Car:{
id: xxx,
Manufacture: {
name: xxx
}
}
}
Another example, given list = [Car.type]
Then I should get
{
Car:{
type: xxx
}
}
I am currently trying to override the serializeAsField method to check if the field is in the given list, but the problem here is that I don't know the depth, then I cannot correctly compare the current field with the list.
How could I achieve it? Are there any other ways?
Mark the unwanted fields with the #JsonIgnore annotation.
On-the-fly filtering
Here is a Baeldung article that discusses using a filter to
determine which fields are serialized:
https://www.baeldung.com/jackson-serialize-field-custom-criteria
I suspect that is the answer you want.
I need help with parsing, I've tried to create a different kind of model classes, but no use, please help me out here. the json looks like this:
[
[
1518909300000,
"0.08815700",
"0.08828700",
"0.08780000",
"0.08792900",
"1727.93100000",
1518910199999,
"152.11480375",
5118,
"897.71600000",
"79.04635703",
"0"
],
[
1518910200000,
"0.08788400",
"0.08824200",
"0.08766200",
"0.08810000",
"1789.81300000",
1518911099999,
"157.20177729",
6201,
"898.89500000",
"78.95697080",
"0"
]
]
and I'm trying to parse it using data class:
#JsonIgnoreProperties(ignoreUnknown = true)
public class KlineResponse {
public List<Kline> getKlineList() {
return klineList;
}
public List<Kline> klineList;
public class Kline {
#JsonProperty("4")
Double close;
#JsonProperty("8")
Integer tradesNumber;
public Double getClose() {
return close;
}
public void setClose(Double close) {
this.close = close;
}
public Integer getTradesNumber() {
return tradesNumber;
}
public void setTradesNumber(Integer tradesNumber) {
this.tradesNumber = tradesNumber;
}
}
}
and this line
mapper.readValue(response.getBody(), new TypeReference<List<KlineResponse>>(){})
or
mapper.readValue(response.getBody(), KlineResponse.class)
but each time the error:
Can not deserialize instance of pt.settings.model.KlineResponse out of START_ARRAY token,
please help
The core issue is that you receive an array of arrays where you expect and array of objects. Changing mapper.readValue(response.getBody(), KlineResponse.class) to mapper.readValue(response.getBody(), Object[].class) confirms it.
You have a couple of options on how to proceed:
Change from Jackson to standard JSON parsing, as suggested by #cricket_007 on his answer
Instead of mapping it to an object try to access the JSON differently. See #jschnasse's answer for an example.
Change the format of text you parse, if you can
If you can't change the format of the input then you can either
Create a constructor and annotate it with #JsonCreator, like instructed here
Parse the input as Object array and feed the parsed array into a constructor of your own
You don't need any java classes. There are no JSON objects to deserialize, only arrays.
In the second case, Jackson is expecting { "klineList": [] }
In the first, [{ "klineList": [] }, { "klineList": [] }]
And a Kline object is only parsable as {"4": 0.0, "8": 0 } (replace zeros with any value of same type)... So really unclear why you expected that to work given that data... The annotations are not the index of the lists.
Plus, your lists have both strings and integers, so you can only deserialize as TypeReference<List<List<Object>>>, then iterate that to parse ints, floats, or strings
I might recommend you use a standard json parser, not an objectmapper
Use JsonNode together with JPointer. Avoid to create a POJO and work directly on the data via JsonNode.
ObjectMapper mapper = new ObjectMapper();
JsonNode matrix = mapper.readValue(in, JsonNode.class);
matrix.forEach(array -> {
System.out.println("Next Values:");
System.out.println(array.at("/4").asDouble());
System.out.println(array.at("/8").asInt());
});
Prints
Next Values:
0.087929
5118.0
Next Values:
0.0881
6201.0
This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 9 years ago.
I am having a hashMap which stores my values like that:
{10997=Event [Numb=0007449001, date=07.02.2008], 10998=Event [Numb=0007449001, date=08.04.2008], ...
As you can see the key is a integer and the value is an Event Object which has a numeric value and a date, which is saved as a String.
I want to compare the numeric value of the map to another value with an if statment.
I tried:
if(numericVal==eventMap.get(i).toString()) {
However that does not really work. How to get the value easily out of the HashMap and of the Object?
I appreciate your answer!
UPDATE
By easily I mean performance orientated, because I have to process a lot of values.
first off; do not compare Strings with == use equalsIgnoreCase() instead (or just equals() if the casing matters)
second; consider giving your Event a compareTo() and while you are at it an equals() and a hashCode().
Once you got the components in place and why they should be there, you'll realize what you want to do is possible in a much more elegant way.
If I understand correctly you are trying to compare the actual numeric value within your EventObject
Therefore your EventObject has to have an accesor to it.
Lets say your EventObject looks something like:
public class EventObject {
private String numericValue;
private String date;
public EventObject(String numericValue, String date) {
this.numericValue = numericValue;
this.date = date;
}
public String getNumericValue() {
return numericValue;
}
public void setNumericValue(String numericValue) {
this.numericValue = numericValue;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
From there now accessing the numericValue from a HashMap should be as easy as:
HashMap<Integer,EventObject> map = new HashMap<>();
map.put(10997,new EventObject("0007449001", "07.02.2008"));
int keyMap = 10997; //for sake of making obvious this is Key and not numericValue
String numValue = hashMap.get(keyMap).getNumericValue();
A comparison then can be done by:
if("0007449001".equals(map.get(10997).getNumericValue())) {
System.out.println("The Date I have is: " + map.get(10997).getDate());
}
Which will get you:
The Date I have is: 07.02.2008
I have json string represenatation of some object
class objects is
public class SMPBBaseObjectsList {
public ArrayList<Object> data = new ArrayList<>();
public Integer count;
public Integer limitFrom;
public Integer limitTo;
public Boolean hasMore;
public String dataItemsClass;
}
And i have json
{"classItem":"smpb.utility.classes.SMPBBaseObjectsList","dataItemsClass":"smpb.base.classes.SMPBUser","dataSliceCode":"012013","data":[{"id":1374046117510970000,"Name":"Test3","classItem":"smpb.base.classes.SMPBUser","dataSliceCode":"012013"}],"filter":{"orderItems":[],"filterItems":[]}}
I try parse this json and create object of my class with next code:
String json = "{\"classItem\":\"smpb.utility.classes.SMPBBaseObjectsList\",\"dataItemsClass\":\"smpb.base.classes.SMPBUser\",\"dataSliceCode\":\"012013\",\"data\":[{\"id\":1374046117510970000,\"Name\":\"Test3\",\"classItem\":\"smpb.base.classes.SMPBUser\",\"dataSliceCode\":\"012013\"}],\"filter\":{\"orderItems\":[],\"filterItems\":[]}}";
SMPBBaseObjectsList list = new GsonBuilder().create().fromJson(json, SMPBBaseObjectsList.class);
System.out.println("BEFORE:" + json);
System.out.println("AFTER: " + list);
System outputs:
BEFORE:{"classItem":"smpb.utility.classes.SMPBBaseObjectsList","dataItemsClass":"smpb.base.classes.SMPBUser","dataSliceCode":"012013","data":[{"id":1374044905885298000,"Name":"Test3","classItem":"smpb.base.classes.SMPBUser","dataSliceCode":"012013"}],"filter":{"orderItems":[],"filterItems":[]}}
AFTER: {"classItem":"smpb.utility.classes.SMPBBaseObjectsList","dataItemsClass":"smpb.base.classes.SMPBUser","dataSliceCode":"012013","data":[{"Name":"Test3","id":1.374044905885298011E18,"classItem":"smpb.base.classes.SMPBUser","dataSliceCode":"012013"}],"filter":{"orderItems":[],"filterItems":[]}}
As u can see in Json String i have ID with value 1374044905885298000 , but when object serialized from string i got 1.374044905885298011E18
And problem is what this representation of Long lost last zeros 0000 and i got Long 1374044905885297920
Why? and how fix it?
Data in Array is String map, and it's already all Long id Double format.
I try registerAdapater for Long or Double but never triggered.
Version of Gson 2.2.4
UPDATE
It's not duplicate of question
How to prevent Gson from converting a long number (a json string ) to scientific notation format?
I can't tell exactly what the problem is, but you can solve it by creating another class, i.e. Data to use in the List instead of the Object class... I tried this code and it's working fine for me!
So, you need to replace the ArrayList<Object> in your SMPBBaseObjectsList by:
public ArrayList<Data> data = new ArrayList<>()
And create a new class like this:
public class Data {
public Long id;
public String Name;
public String classItem;
public String dataSliceCode;
}
I guess there's an issue when parsing the JSON to an Object object, it probably makes some conversion that leads to that number formatting, but unfortunately I'm not an expert in this Java low-level issues...
Anyway, with this code you are explicitly specifying that you want that value parsed into a Long, so there's no problem!