JSON decoding error: Cannot deserialize value of type `java.math.BigInteger` from Object value (token `JsonToken.START_OBJECT`); (Jackson) - spring-boot

It is necessary to deserialize the result from Mono<ResultSumDto> to JSON, then to sent to the client as JSON.
Controller
#GetMapping("v1/sequence/{startRange}/{endRange}")
Mono<ResultSumDto > getSumFromRange(
#PathVariable BigInteger startRange,
#PathVariable BigInteger endRange) {
ResultSumDto resultSumDto = ...
return Mono.just(resultSumDto);
}
#Configuration
public class JacksonObjectMapperConfiguration {
#Autowired
public void serializeBigInteger(ObjectMapper objectMapper) {
JsonFormat.Value formatValue =
JsonFormat.Value.forShape(JsonFormat.Shape.STRING);
objectMapper
.configOverride(BigInteger.class)
.setFormat(formatValue);
}
}
#Data
#Builder
public class ResultSumDto {
private final BigInteger sumSeq;
private final BigInteger [] seqRange;
private final Boolean isCached;
}
private Mono<ResultSumDto> buildResult(SeqDto dto) {
Mono<BigInteger> sumSeq =
calculateSumRangeValuesFibonacciSequence(dto);
BigInteger bigInteger = null;
try {
bigInteger = sumSeq
.toFuture()
.get();
} catch (InterruptedException | ExecutionException e) {
log.error(e.getLocalizedMessage());
Thread.currentThread().interrupt();
}
BigInteger[] rangeGiven = new BigInteger[]
{dto.getStartRange(), dto.getEndRange()};
return Mono.just(ResultSumSeqDto.builder()
.sumSequence(bigInteger)
.sequenceRange(rangeGiven)
.isCached(false)
.build()
);
}
But I have a mistake:
org.springframework.core.codec.DecodingException: JSON decoding error: Cannot deserialize value of type java.math.BigInteger from Object value (token JsonToken.START_OBJECT); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize value of type java.math.BigInteger from Object value (token JsonToken.START_OBJECT)
at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 1]
But after all, when I get values in endpoint, serialization to the BigInteger type goes without problems.
Who has any idea why it doesn't work and how it can be fixed. Share your knowledge on how to deserialize an array BigInteger and a field with the BigInteger type?

That's what worked in my case.
public class DeserializeResultCalculateSumSequence
extends StdDeserializer<ResultCalculateSumSequenceDto> {
public DeserializeResultCalculateSumSequence() {
this(null);
}
protected DeserializeResultCalculateSumSequence(Class<?> vc) {
super(vc);
}
#Override
public ResultCalculateSumSequenceDto deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext)
throws IOException, JacksonException {
JsonNode node = jsonParser
.getCodec()
.readTree(jsonParser);
BigInteger sumSequence = node
.get("sumSequence")
.bigIntegerValue();
ObjectMapper mapper = new ObjectMapper();
String sequenceRangeStr = node.get("sequenceRange").toString();
BigInteger[] sequenceRange = mapper
.readValue(sequenceRangeStr, BigInteger[].class);
boolean isCached = node
.get("isCached")
.asBoolean();
return ResultCalculateSumSequenceDto
.builder()
.sumSequence(sumSequence)
.sequenceRange(sequenceRange)
.isCached(isCached)
.build();
}
}
#Data
#Builder
#JsonDeserialize(using = DeserializeResultCalculateSumSequence.class)
public class ResultCalculateSumSequenceDto {
private final BigInteger sumSequence;
private final BigInteger [] sequenceRange;
private final Boolean isCached;
}

Related

no String-argument constructor/factory method to deserialize from String value with spring boot client

I am using spring boot application with frontend (spring boot application using thymeleaf) and backend (spring boot REST application ) are separated using REST api. The frontend uses HttpClient to send request to backend. Whenever I try to update an object the HttpClient creates an error for json parsing. The request is not accepted by the backend (ProcessDTORequest object ) with error as follows.
The exception is as follows:
{"message":"JSON parse error: Cannot construct instance of `com.app.dataaccess.entity.Process` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('68d22e4d-7116-4130-aa06-9ba120aadc66'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.app.dataaccess.entity.Process` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('68d22e4d-7116-4130-aa06-9ba120aadc66')\n at [Source: (PushbackInputStream); line: 1, column: 10310] (through reference chain: com.app.ui.dto.request.ProcessDTORequest[\"answeredQuestionnaires\"]->java.util.HashSet[0]->com.app.dataaccess.entity.AnsweredQuestionnaire[\"process\"])","httpStatus":"INTERNAL_SERVER_ERROR","timeStamp":"2022-11-04T08:44:35.9108286Z"}
HttpClient method for post request is as follows:
public String executePost(
final String url, final Object payLoad, final Map<String, String> headers,
final Map<String, String> params) throws Exception {
final CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// Add query strings to URL
URIBuilder builder = new URIBuilder(url);
for (final Map.Entry<String, String> elm : params.entrySet()) {
builder = builder.setParameter(elm.getKey(), elm.getValue());
}
// can change for HttpPut, HttpPost, HttpPatch
final HttpPost request = new HttpPost(builder.build());
// Add headers from input map
for (final Map.Entry<String, String> elm : headers.entrySet()) {
request.addHeader(elm.getKey(), elm.getValue());
}
request.setHeader("Accept", "application/json");
request.setHeader("Content-type", "application/json");
// Send Json String as body, can also send UrlEncodedFormEntity
final StringEntity entity = new StringEntity(objectMapper.writeValueAsString(payLoad));
request.setEntity(entity);
try {
final CloseableHttpResponse response = httpClient.execute(request);
System.out.println("Return response status code: "+response.getStatusLine().getStatusCode());
System.out.println("Return response status code: "+response.getStatusLine());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// Read response string using EntityUtils class of Apache http client library
// Serialize json string into map or any other object
return EntityUtils.toString(response.getEntity());
} else {
throw new Exception(EntityUtils.toString(response.getEntity()));
// throw new Exception(String.format("Response status code was and response was ",
// response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity())));
}
} catch (final ClientProtocolException e) {
throw new Exception("Client protocol Exception occurred while executing request", e);
} catch (final Exception e) {
System.out.println(e);
throw new Exception(e);
}
}
I used the configuration for object mapper as follows:
#Configuration
public class AppConfig {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
objectMapper.registerModule(new JavaTimeModule());
return objectMapper; }
}
Process.java (this is used for serializing/deserializing)
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
public class Process {
private UUID processId;
private List<User> users = new ArrayList<>();
private List<UnitType> units = new ArrayList<>();
private String furtherComment;
private List<AnsweredQuestionnaire> answeredQuestionnaires = new ArrayList<>()
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Process)) return false;
Process process = (Process) o;
return getProcessId().equals(process.getProcessId());
}
#Override
public int hashCode() {
return Objects.hash(getProcessId());
}
}
The json from the server is like the following
{
"#id": "bba35e58-5d4b-44ce-9a5a-486f55f79af7",
"processId": "21ef7f9d-4fcc-417c-96e8-4327206d2592",
"users": [
{
"#id": "69d2f392-8213-4f34-9cb5-f0c403170787",
"userId": "5a17ec5f-c20a-4873-93af-bf69fad4eb26",
"roles": [
{
"roleId": "f6ad33a7-9d03-4260-81c2-a4a4c791e30a",
"users": []
}
],
"processes": []
}
],
"units": [
{
"unitTypeId": "c784d197-1dc7-446e-b3e5-6468a7954878",
"unit": {
"unitId": "aba76d05-e2ea-4b5a-828b-349966595258"
},
"isResponsibleUnit": true
}
],
"furtherComment": "",
"answeredQuestionnaires": [
{
"#id": "7ca1af09-eefd-4c56-9587-581858fbbc57"
}
]
}
The relation between the entities Process, AnsweredQuestionnaire and User is as follows:
Between Process and AnsweredQuestionnaire (One-to-many) respectively.
Between Process and User (many-to-many).
Between Process and UnitType (one-to-many) respectively.
AnsweredQuestionnaire.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class AnsweredQuestionnaire {
private UUID answeredQuestionnaireId;
private Questionnaire questionnaire;
private Process process;
public void addProcessToAnsweredQuestionnaire(Process process){
//remove old association
if(this.process != null){
this.process.getAnsweredQuestionnaires().remove(this);
}
this.process = process;
//add new association
if(process != null){
this.process.getAnsweredQuestionnaires().add(this);
}
}
}
User.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class User {
private UUID userId;
private String firstName;
private String lastName;
private String phoneNumber;
private String email;
private List<Role> roles = new ArrayList<>();
private List<Process> processes = new ArrayList<>();
public void addProcessToUser(Process process){
this.processes.add(process);
process.getUsers().add(this);
}
public void removeProcessFromUser(Process process){
this.processes.remove(process);
process.getUsers().remove(this);
}
}
ProcessDTORequest.java (this class is on the backend accepting the request from the frontend)
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class ProcessDTORequest {
private UUID processId;
private Set<User> users = new HashSet<>();
private Set<AnsweredQuestionnaire> answeredQuestionnaires = new HashSet<>();
private Set<UnitType> units = new HashSet<>();
}
UnitType.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class UnitType {
private UUID unitTypeId;
private Unit unit;
private Boolean isResponsibleUnit = false;
}

how to cast response to responseEntity

Hello I have a problem with my service says that "Internal Server Error Exception occured! Exception details: IdentificationResponse cannot be cast to org.springframework.http.ResponseEntity"" any help on this matter ?
my code blocks below;
my IdentificationService Impl;
Slf4j
#Service
#RequiredArgsConstructor
public class IdentificationServiceImpl implements IdentificationService {
private final RestTemplate restTemplate;
#Value("${platform.url}")
private String platform;
private final ServiceConfig serviceConfig;
#Override
public ResponseEntity<IdentificationResponse> createIdentification(Request request) {
IdentificationRequest identificationRequest = new IdentificationRequest(serviceConfig.getIssuerKey(), serviceConfig.getSecureKey());
identificationRequest.setOperation(ClientConstants.CREATE_IDENTIFICATION_ADD);
HttpEntity<IdentificationRequest> requestHttpEntity = new HttpEntity<>(IdentificationRequest);
ResponseEntity<IdentificationResponse> response = restTemplate.exchange(platform, HttpMethod.POST, requestHttpEntity, IdentificationResponse.class);
return HandleResponse.handleResponse(response);
}
my handle Response class below;
public static <T> T handleResponse(ResponseEntity response) {
HttpStatus code = response.getStatusCode();
if (code == HttpStatus.OK || code == HttpStatus.CREATED) {
return (T) response.getBody();
} else if (code == HttpStatus.NO_CONTENT) {
return null;
} else if (code == HttpStatus.BAD_REQUEST)
throw new BadRequestException("BadRequest Exception occured while requesting! Exception details: " + response.getBody());
else if (code == HttpStatus.UNAUTHORIZED)
throw new UnauthorizedException("Unauthorized Exception occured while requesting! Exception details: " + response.getBody());
else
throw new HttpClientException("Exception occured! Exception details: " + response.getBody(), code.value());
}
}
my IdentificationRequest class below;
#Data
public class IdentificationRequest {
#Setter(AccessLevel.NONE)
#NotNull
#JsonProperty("IssuerKey")
private String issuerKey;
#Setter(AccessLevel.NONE)
#NotNull
#JsonProperty("SecurityKey")
private String secureKey;
#NotNull
#JsonProperty("TransactionId")
private String transactionId;
#NotNull
#JsonProperty("TransactionDate")
#DateTimeFormat(pattern = "YYYY-MM-DD HH:mm:ss.SSS")
private String transactionDate;
#NotNull
#JsonProperty("Msisdn")
#Pattern(regexp = "^905.{9}", message = "must be of 12 char/digit")
private Long msisdn;
private String operation;
#NotNull
#JsonProperty("Package")
private String Package;
#NotNull
#JsonProperty("STB")
private Boolean isStb;
#JsonProperty("STB_SerialNumber")
private String stbSerialNumber;
#JsonProperty("STB_MacAddress")
private String stbMacAddress;
public IdentificationRequest(String issuerKey, String secureKey) {
this.issuerKey = issuerKey;
this.secureKey = secureKey;
}
}
my Request class below;
#Data
public class Request {
#JsonProperty("OrderId")
private String orderId;
#JsonProperty("OrderItemId")
private String orderItemId;
#JsonProperty("ProcessInstanceId")
private String processId;
#JsonProperty("step_id")
private String stepId;
#JsonProperty("step_name")
private String stepName;
my Response class below;
#Data
public class IdentificationResponse {
#JsonProperty("IsSuccessful")
private Boolean isSuccessful;
#JsonProperty("Code")
private Integer code;
#JsonProperty("Message")
private String message;
#JsonProperty("Data")
private Object data;
}
Do I need a map to response to response Entity?
As per your code, when you call response.getBody() method you'll get IdentificationResponse and it is converting it to ResponseEntity which is throwing ClassCastException.
if (code == HttpStatus.OK || code == HttpStatus.CREATED)
return (T) response.getBody();
You can either return response directly instead of response.getBody() to resolve your issue or you can follow the below implementation to your method if you want your createIdentification method to return IdentificationResponse instance
return HandleResponse.handleResponse(response, IdentificationResponse.class);
public static <T> T handleResponse(ResponseEntity response, Class<T> type) {
HttpStatus code = response.getStatusCode();
if (code == HttpStatus.OK || code == HttpStatus.CREATED)
return (T) response.getBody();
}
You can find some more info related to generics here
Basically you are trying to cast an object to an incompatible one, the cast could be done if both objects implement the same interface or if the hierarchy of objects allows it, if this cast cannot be achieved, it is a good idea to create a instance of the response object based on the value of the object that you retrieved.

JPA criteria builder equal method is not working as expected

In my case I'm trying to fetch some data by extracting a value from a json column in db. My code is as follows,
criteriaBuilder.equal(criteriaBuilder.function("JSON_EXTRACT", Boolean.class, root.get("result"), criteriaBuilder.literal("$.matched")), false);
Above code gives me an empty set of data. Also this is working fine in query console.
But,
criteriaBuilder.between(criteriaBuilder.function("JSON_EXTRACT", Double.class, root.get("result"), criteriaBuilder.literal("$.streaming_threshold")), 0.1, 0.9);
this between method is working fine. What could be the mistake here?
UPDATE
Boolean values are the values that I couldn't read. NOT INTEGERS. My JSON structure,
{
"status": "SUCCESS",
"request_id": "request_id",
"time_taken": 8454,
"matched": false,
"streaming_threshold": 0.5
}
I was not able to get it working with the raw boolean field. I converted the Boolean property on the object to String and vice-versa using #JsonSerialize and #JsonDeserialize and then persisted that as json and followed the same approach you did but now searching String.class as false instead of Boolean. My solution is as below:
Entity
#Entity
#Table(name = "json_container")
public class JsonContainer {
#Id
#GeneratedValue
#Type(type = "uuid-char")
private UUID id;
#Column(columnDefinition = "json", name = "json_data")
private String jsonData;
public UUID getId() {
return id;
}
public String getJsonData() {
return jsonData;
}
public void setJsonData(String jsonData) {
this.jsonData = jsonData;
}
public static class SampleDetails {
private String status;
private String requestId;
private Integer timeTaken;
#JsonSerialize(using = StringBooleanJsonSerializer.class)
#JsonDeserialize(using = StringBooleanJsonDeserializer.class)
private Boolean matched;
private Double streamingThreshold;
public SampleDetails() {
}
public SampleDetails(String status, String requestId, Integer timeTaken, Boolean matched, Double streamingThreshold) {
this.status = status;
this.requestId = requestId;
this.timeTaken = timeTaken;
this.matched = matched;
this.streamingThreshold = streamingThreshold;
}
public String getStatus() {
return status;
}
public String getRequestId() {
return requestId;
}
public Integer getTimeTaken() {
return timeTaken;
}
public Double getStreamingThreshold() {
return streamingThreshold;
}
public Boolean getMatched() {
return matched;
}
static class StringBooleanJsonSerializer extends JsonSerializer<Boolean> {
#Override
public void serialize(Boolean value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value != null && value ? value.toString() : "false");
}
}
static class StringBooleanJsonDeserializer extends JsonDeserializer<Boolean> {
#Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
try {
return Boolean.parseBoolean(p.getText());
} catch (RuntimeException e) {
return Boolean.FALSE;
}
}
}
}
Test Class
class MySQLJsonConverterTest {
#Autowired
private EntityManager entityManager;
#Rollback(false)
#Test
void testCustomJsonConverter() throws JsonProcessingException {
JsonContainer jsonContainer = new JsonContainer();
jsonContainer.setJsonData(
getAsJson(new JsonContainer.SampleDetails("success", "12344567", 8454, false, 0.1)));
entityManager.persist(jsonContainer);
Assertions.assertNotNull(jsonContainer.getId());
jsonContainer = new JsonContainer();
jsonContainer.setJsonData(
getAsJson(new JsonContainer.SampleDetails("success", "8989", 121, true, 0.5)));
entityManager.persist(jsonContainer);
Assertions.assertNotNull(jsonContainer.getId());
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<JsonContainer> criteriaQuery = criteriaBuilder.createQuery(JsonContainer.class);
Root<JsonContainer> from = criteriaQuery.from(JsonContainer.class);
criteriaQuery.where(criteriaBuilder.equal(criteriaBuilder.function("JSON_EXTRACT", String.class, from.get("jsonData"),
criteriaBuilder.literal("$.matched")), "false"));
TypedQuery<JsonContainer> typedQuery = entityManager.createQuery(criteriaQuery);
List<JsonContainer> resultList = typedQuery.getResultList();
Assertions.assertEquals(1, resultList.size());
}
private String getAsJson(JsonContainer.SampleDetails sampleDetails) throws JsonProcessingException {
//var created so debugging is ez
String json = new ObjectMapper().writeValueAsString(sampleDetails);
return json;
}
}
Default JPA convert boolean to 0/1, so if serialize boolean to 0/1 into database, the equal query will be ok.

How to de-serialize POJO contains HashTable?

I have pojo like this:
public class Test implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private String hash;
private java.util.Hashtable<Integer, Long> myTempTable;
public java.util.Hashtable<Integer, Long> getMyTempTable() {
return this.myTempTable;
}
public void setMyTempTable(java.util.Hashtable<Integer, Long> myTempTable) { this.myTempTable = myTempTable; }
//And some few variables
}
In response I get this POJO in JSON format but while converting this JSON to "Test" java object like this.
gson.fromJson(tempString, Test.class);
It is giving error as
java.lang.IllegalArgumentException: Can not set java.util.Hashtable field <package_name>.Temp.myTempTable to java.util.LinkedHashMap
Why GSON is converting HashTable to LinkedHashMap?
And does this error means?
UPDATE: JSON File as
{
"hash": "abc",
"myTempTable": {
"1": 30065833999,
"2": 34364325903,
"3": 536872959
}
}
For converting an Object to JSON String.
public static <T> String convertObjectToStringJson(T someObject, Type type) {
Gson mGson = new Gson();
String strJson = mGson.toJson(someObject, type);
return strJson;
}
For converting a JSON String to an Object.
public static <T> T getObjectFromJson(String json, Type type) {
Gson mGson = new Gson();
if (json != null) {
if (json.isEmpty()) {
return null;
}
}
return mGson.fromJson(json, type);
}
where
Type is type of your Object.
ex:
for object:
new TypeToken<YOUR_POJO>(){}.getType();
for list:
new TypeToken<List<YOUR_POJO>>(){}.getType();

Rest Json Jackson Mapper Custom Object Mapper

I am having an issue with the Jackson Json mapper which I can't figure out how to solve.
I am having a Spring MVC Rest application and the endpoints are converted to Json using Jackson.
Some of the result objects contain a type that I want to tamper with before it gets converted.
More specifically, a result object could look like this.
ResultObject
- getDoubleMap() : DoubleMap
- getDoubleEntries() : List<DoubleEntry>
- toMap() : Map<String, Double>
What I want to do is to not have Jackson convert the DoubleMap instance but much rather override it like this
Object someJacksonMapInterceptor(Object object) {
if(object instanceof DoubleMap) {
return ((DoubleMap) object).toMap();
}
return object;
}
I have tortured google quite a while now and not a simple solution. Hope someone can advise.
Many thanks in advance.
In one application, we are custom-deserealizing date, probably you can use it for your custom deserealization.
public class VitalSign {
public static final String DATE_FORMAT1 = "yyyy-MM-dd'T'HH:mm:ssZ";
public static final String DATE_FORMAT2 = "yyyy-MM-dd'T'HH:mm:ss";
//public static final String DATE_FORMAT3 = "yyyy-MM-dd'T'HH:mm:ssTDZ";
public static final String DATE_FORMAT4 = "MMM dd, yyyy h:mm:ss aa";
#NotNull
#Column(name = "observed")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(style = "M-")
#JsonDeserialize(using = CustomJsonDateDeserializer.class)
private Date timestamp;
public static class CustomJsonDateDeserializer extends JsonDeserializer<Date> {
public CustomJsonDateDeserializer() {
super();
}
#Override
public Date deserialize(JsonParser jsonparser, DeserializationContext deserializationcontext) throws IOException, JsonProcessingException {
SimpleDateFormat[] formats = { new SimpleDateFormat(DATE_FORMAT1), new SimpleDateFormat(DATE_FORMAT2), new SimpleDateFormat(DATE_FORMAT4, Locale.US) };
String date = jsonparser.getText();
for (SimpleDateFormat format : formats) {
try {
return format.parse(date);
} catch (ParseException e) {
}
}
throw new RuntimeException("Unparseable date " + date);
}
}
}
For serializing, you can just annotate your toMap() method with #JsonValue. For deserializing, if you have a static factory to create a DoubleMap from a Map<String, Double>, you can just annotate that with #JsonCreator.
private final ObjectMapper mapper = new ObjectMapper();
#Test
public void serialize_doublemap() throws Exception {
DoubleMap map = new DoubleMap();
map.put("red", 0.5);
map.put("orange", 0.7);
assertThat(mapper.writeValueAsString(map), equivalentTo("{ red: 0.5, orange: 0.7 }"));
}
#Test
public void deserialize_doublemap() throws Exception {
assertThat(mapper.readValue("{ \"red\": 0.5, \"orange\": 0.7 }", DoubleMap.class).toMap(),
equalTo(ImmutableMap.of("red", 0.5, "orange", 0.7)));
}
public static class DoubleMap {
public List<DoubleEntry> entries = new ArrayList<>();
public void put(String label, double value) {
entries.add(new DoubleEntry(label, value));
}
#JsonCreator
public static DoubleMap fromJson(Map<String, Double> input) {
DoubleMap map = new DoubleMap();
input.forEach(map::put);
return map;
}
public List<DoubleEntry> getDoubleEntries() {
return entries;
}
#JsonValue
public Map<String, Double> toMap() {
return entries.stream().collect(Collectors.toMap(e -> e.label, e -> e.value));
}
}
public static final class DoubleEntry {
public final String label;
public final double value;
public DoubleEntry(String label, double value) {
this.label = label;
this.value = value;
}
}

Resources