Skip to content

Commit 1d19e99

Browse files
authored
Merge pull request #370 from EasyPost/EXP-761_stateless_rate_deserializer
fix: StatelessRateDeserializer and WebhookDeserializer for underscore fields
2 parents ded379c + 8df3260 commit 1d19e99

File tree

9 files changed

+91
-51
lines changed

9 files changed

+91
-51
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## v8.5.1 (2026-01-08)
4+
5+
- Corrects `StatelessRateDeserializer` and `WebhookDeserializer` to treat all camelCase fields like all other models to properly deserialize JSON fields containing underscors
6+
37
## v8.5.0 (2025-12-09)
48

59
- Adds the following functions:
@@ -12,6 +16,7 @@
1216
## v8.4.1 (2025-12-01)
1317

1418
- Adds missing `apiKeys` field to `BaseUser`
19+
- Removes unecessary `SerializedName` entries in `StatelessRate`
1520

1621
## v8.4.0 (2025-11-24)
1722

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Add this to your project's POM:
1616
<dependency>
1717
<groupId>com.easypost</groupId>
1818
<artifactId>easypost-api-client</artifactId>
19-
<version>8.5.0</version>
19+
<version>8.5.1</version>
2020
</dependency>
2121
```
2222

@@ -25,7 +25,7 @@ Add this to your project's POM:
2525
Add this to your project's build file:
2626

2727
```groovy
28-
implementation "com.easypost:easypost-api-client:8.5.0"
28+
implementation "com.easypost:easypost-api-client:8.5.1"
2929
```
3030

3131
**NOTE:** [Google Gson](http://code.google.com/p/google-gson/) is required.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
8.5.0
1+
8.5.1

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<groupId>com.easypost</groupId>
77
<artifactId>easypost-api-client</artifactId>
88

9-
<version>8.5.0</version>
9+
<version>8.5.1</version>
1010
<packaging>jar</packaging>
1111

1212
<name>com.easypost:easypost-api-client</name>

src/main/java/com/easypost/Constants.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.easypost;
22

3+
import java.util.HashMap;
4+
import java.util.List;
5+
36
import com.easypost.exception.APIException;
47
import com.easypost.http.HashMapSerializer;
58
import com.easypost.model.AddressVerification;
@@ -16,9 +19,6 @@
1619
import com.google.gson.Gson;
1720
import com.google.gson.GsonBuilder;
1821

19-
import java.util.HashMap;
20-
import java.util.List;
21-
2222
public abstract class Constants {
2323

2424
public static final String EASYPOST_SUPPORT_EMAIL = "[email protected]";
@@ -62,12 +62,11 @@ public abstract static class ErrorCodes {
6262

6363
public abstract static class CarrierAccountTypes {
6464
public static final List<String> CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_WORKFLOW = ImmutableList.of(
65-
"FedexAccount", "FedexSmartpostAccount"
66-
);
65+
"FedexAccount", "FedexSmartpostAccount");
6766

6867
public static final List<String> CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_OAUTH = ImmutableList.of(
69-
"AmazonShippingAccount", "UpsAccount", "UpsMailInnovationsAccount", "UpsSurepostAccount", "UspsShipAccount"
70-
);
68+
"AmazonShippingAccount", "UpsAccount", "UpsMailInnovationsAccount", "UpsSurepostAccount",
69+
"UspsShipAccount");
7170
}
7271

7372
public abstract static class Http {
@@ -77,13 +76,18 @@ public abstract static class Http {
7776
public static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = 60000;
7877

7978
public static final Gson GSON = new GsonBuilder()
79+
// Standard model deserializer
8080
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
81+
// Core (de)serializers
8182
.registerTypeAdapter(HashMap.class, new HashMapSerializer())
82-
.registerTypeAdapter(SmartRateCollection.class, new SmartRateCollectionDeserializer())
8383
.registerTypeAdapter(APIException.class, new ErrorDeserializer())
84+
// Model custom deserializers
8485
.registerTypeAdapter(AddressVerification.class, new AddressVerificationDeserializer())
86+
.registerTypeAdapter(SmartRateCollection.class, new SmartRateCollectionDeserializer())
8587
.registerTypeAdapter(StatelessRate[].class, new StatelessRateDeserializer())
86-
.registerTypeAdapter(Webhook[].class, new WebhookDeserializer()).create();
88+
.registerTypeAdapter(Webhook[].class, new WebhookDeserializer())
89+
.create();
90+
8791
public static final Gson PRETTY_PRINT_GSON = new GsonBuilder().setPrettyPrinting().serializeNulls()
8892
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
8993
}
Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package com.easypost.model;
22

3-
import com.google.gson.Gson;
3+
import java.lang.reflect.Type;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
import com.easypost.Constants;
48
import com.google.gson.JsonDeserializationContext;
59
import com.google.gson.JsonDeserializer;
610
import com.google.gson.JsonElement;
711
import com.google.gson.JsonObject;
812
import com.google.gson.JsonParseException;
913

10-
import java.lang.reflect.Type;
11-
1214
public final class StatelessRateDeserializer implements JsonDeserializer<StatelessRate[]> {
1315
/**
1416
* Deserialize a StatelessRate from a JSON object.
@@ -21,11 +23,20 @@ public final class StatelessRateDeserializer implements JsonDeserializer<Statele
2123
*/
2224
@Override
2325
public StatelessRate[] deserialize(final JsonElement json, final Type typeOfT,
24-
final JsonDeserializationContext context) throws JsonParseException{
26+
final JsonDeserializationContext context) throws JsonParseException {
2527
JsonObject jo = json.getAsJsonObject();
2628
JsonElement results = jo.get("rates");
27-
Gson gson = new Gson();
2829

29-
return gson.fromJson(results, StatelessRate[].class);
30+
if (results == null || !results.isJsonArray()) {
31+
return new StatelessRate[0];
32+
}
33+
34+
List<StatelessRate> ratesList = new ArrayList<>();
35+
for (JsonElement element : results.getAsJsonArray()) {
36+
StatelessRate rate = Constants.Http.GSON.fromJson(element, StatelessRate.class);
37+
ratesList.add(rate);
38+
}
39+
40+
return ratesList.toArray(new StatelessRate[0]);
3041
}
3142
}
Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
package com.easypost.model;
22

3-
import com.google.gson.Gson;
3+
import java.lang.reflect.Type;
4+
import java.util.ArrayList;
5+
import java.util.List;
6+
7+
import com.easypost.Constants;
48
import com.google.gson.JsonDeserializationContext;
59
import com.google.gson.JsonDeserializer;
610
import com.google.gson.JsonElement;
711
import com.google.gson.JsonObject;
812
import com.google.gson.JsonParseException;
913

10-
import java.lang.reflect.Type;
11-
1214
public final class WebhookDeserializer implements JsonDeserializer<Webhook[]> {
1315
/**
1416
* Deserialize a list of Webhook from a JSON object.
@@ -21,11 +23,20 @@ public final class WebhookDeserializer implements JsonDeserializer<Webhook[]> {
2123
*/
2224
@Override
2325
public Webhook[] deserialize(final JsonElement json, final Type typeOfT,
24-
final JsonDeserializationContext context) throws JsonParseException{
26+
final JsonDeserializationContext context) throws JsonParseException {
2527
JsonObject jo = json.getAsJsonObject();
2628
JsonElement results = jo.get("webhooks");
27-
Gson gson = new Gson();
2829

29-
return gson.fromJson(results, Webhook[].class);
30+
if (results == null || !results.isJsonArray()) {
31+
return new Webhook[0];
32+
}
33+
34+
List<Webhook> webhooksList = new ArrayList<>();
35+
for (JsonElement element : results.getAsJsonArray()) {
36+
Webhook webhook = Constants.Http.GSON.fromJson(element, Webhook.class);
37+
webhooksList.add(webhook);
38+
}
39+
40+
return webhooksList.toArray(new Webhook[0]);
3041
}
3142
}

src/test/java/com/easypost/BetaRateTest.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
11
package com.easypost;
22

3-
import com.easypost.exception.EasyPostException;
4-
import com.easypost.model.StatelessRate;
5-
import com.easypost.utils.Utilities;
6-
import org.junit.jupiter.api.BeforeAll;
7-
import org.junit.jupiter.api.Test;
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import static org.junit.jupiter.api.Assertions.assertTrue;
86

97
import java.util.Arrays;
108
import java.util.HashMap;
119
import java.util.List;
1210

13-
import static org.junit.jupiter.api.Assertions.assertEquals;
14-
import static org.junit.jupiter.api.Assertions.assertThrows;
15-
import static org.junit.jupiter.api.Assertions.assertTrue;
11+
import org.junit.jupiter.api.BeforeAll;
12+
import org.junit.jupiter.api.Test;
13+
14+
import com.easypost.exception.EasyPostException;
15+
import com.easypost.model.StatelessRate;
16+
import com.easypost.utils.Utilities;
1617

1718
public class BetaRateTest {
1819
private static TestUtils.VCR vcr;
@@ -40,7 +41,10 @@ public void testRetrieveStatelessRates() throws EasyPostException {
4041

4142
List<StatelessRate> rates = vcr.client.betaRate.retrieveStatelessRates(shipment);
4243

43-
assertTrue(rates.stream().allMatch(rate -> rate != null));
44+
// Test that deserialization worked by accessing a field with an underscore
45+
for (StatelessRate rate : rates) {
46+
assertTrue(rate.getListRate() != null);
47+
}
4448
}
4549

4650
/**
@@ -60,8 +64,8 @@ public void testRetrieveLowestStatelessRate() throws EasyPostException {
6064
assertEquals("GroundAdvantage", lowestRate.getService());
6165

6266
List<String> carriers = Arrays.asList("invalidCarrierName");
63-
EasyPostException exception =
64-
assertThrows(EasyPostException.class, () -> Utilities.getLowestStatelessRate(rates, carriers, null));
67+
EasyPostException exception = assertThrows(EasyPostException.class,
68+
() -> Utilities.getLowestStatelessRate(rates, carriers, null));
6569

6670
assertEquals("No rates found.", exception.getMessage());
6771
}

src/test/java/com/easypost/WebhookTest.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
package com.easypost;
22

3-
import com.easypost.exception.EasyPostException;
4-
import com.easypost.model.Event;
5-
import com.easypost.model.Webhook;
6-
import com.easypost.model.WebhookCustomHeader;
7-
import com.easypost.utils.Utilities;
8-
import com.google.common.collect.ImmutableMap;
3+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
4+
import static org.junit.jupiter.api.Assertions.assertEquals;
5+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
6+
import static org.junit.jupiter.api.Assertions.assertThrows;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
import java.util.HashMap;
10+
import java.util.List;
11+
import java.util.Map;
12+
913
import org.junit.jupiter.api.AfterEach;
1014
import org.junit.jupiter.api.BeforeAll;
1115
import org.junit.jupiter.api.MethodOrderer;
1216
import org.junit.jupiter.api.Test;
1317
import org.junit.jupiter.api.TestMethodOrder;
1418

15-
import java.util.HashMap;
16-
import java.util.List;
17-
import java.util.Map;
18-
19-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
20-
import static org.junit.jupiter.api.Assertions.assertEquals;
21-
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
22-
import static org.junit.jupiter.api.Assertions.assertThrows;
23-
import static org.junit.jupiter.api.Assertions.assertTrue;
19+
import com.easypost.exception.EasyPostException;
20+
import com.easypost.model.Event;
21+
import com.easypost.model.Webhook;
22+
import com.easypost.model.WebhookCustomHeader;
23+
import com.easypost.utils.Utilities;
24+
import com.google.common.collect.ImmutableMap;
2425

2526
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
2627
public final class WebhookTest {
@@ -119,6 +120,10 @@ public void testAll() throws EasyPostException {
119120

120121
assertTrue(webhooks.size() > 0);
121122
assertTrue(webhooks.stream().allMatch(webhook -> webhook != null));
123+
// Test that deserialization worked by accessing a field with an underscore
124+
for (Webhook webhook : webhooks) {
125+
assertTrue(webhook.getCreatedAt() != null);
126+
}
122127
}
123128

124129
/**

0 commit comments

Comments
 (0)