14664

Gson: How do I parse polymorphic values that can be either lists or strings?

Question:

I need to parse a JSON file that contains long list of customers. In the JSON file each customer may have one id as a string:

{ "cust_id": "87655", ... },

or a few ids as an array:

{ "cust_id": [ "12345", "45678" ], ... },

The Customer class is as below:

<pre class="lang-java prettyprint-override">public class Customer { @SerializedName("cust_id") @Expose private String custId; public String getCustId() { return custId; } public void setCustId(String custId) { this.custId = custId; } }

I parse the JSON using Gson:

Gson gson = new Gson() Customers customers1 = gson.fromJson(json, Customers.class)

and it fails with com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a string but was BEGIN_ARRAY when it attempts to parse the array.

The reason of failure is clear.

My question: what is the best way to handle both cases (when id is a string and when it is an array of strings), given <strong>I can not change the json file structure</strong>?

Answer1:

If you want to handle both scenarios you can use a custom deserializer. Of course, you have to change the "cust_id" variable to be a list or an array.

Main:

String json1 = "{\"cust_id\": \"87655\"}"; String json2 = "{\"cust_id\": [\"12345\", \"45678\"]}"; GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Customer.class, new CustomerDeserializer()); Gson gson = gsonBuilder.create(); Customer customer1 = gson.fromJson(json1, Customer.class); System.out.println(customer1); Customer customer2 = gson.fromJson(json2, Customer.class); System.out.println(customer2);

Customer

public class Customer { @SerializedName("cust_id") private List<String> custId; public List<String> getCustId() { return custId; } public void setCustId(List<String> custId) { this.custId = custId; } }

CustomerDeserializer

public class CustomerDeserializer implements JsonDeserializer<Customer> { @Override public Customer deserialize(JsonElement jsonElement, Type typeOf, JsonDeserializationContext context) throws JsonParseException { Customer result = null; Gson gson = new Gson(); try { // try to deserialize by assuming JSON has a list result = gson.fromJson(jsonElement, Customer.class); } catch (JsonSyntaxException jse) { // error here means JSON has a single string instead of a list try { // get the single ID String custId = jsonElement.getAsJsonObject().get("cust_id").getAsString(); result = new Customer(); result.setCustId(Arrays.asList(new String[] {custId})); } catch (Exception e) { // more error handling here e.printStackTrace(); } } return result; }

}

Output

Customer [custId=[87655]] Customer [custId=[12345, 45678]]

Answer2:

Try method overriding:

public class Customer { @SerializedName("cust_id") @Expose private String custId; public void setCustId(String custId) { this.custId = {custId}; } public String[] getCustId() { return custId; } @override public void setCustId(String[] custId) { this.custId = custId; } }

Now In the code all values of CUSTID will be arrays instead of strings

Answer3:

You can just simply specify all values as array, even if is just one value.

{ "cust_id": ["87655"], ... },

UPDATE: If you cannot change the json, you can bind every field in Customer class except custId and set it manually.

public class Customer { private String[] custId; public String getCustId() { return custId; } public void setCustId(String custId) { custId = new String[] {custId}; } public void setCustId(String[] custId) { this.custId = custId; } }

And then parse manually:

Gson gson = new Gson(); Customers customers = gson.fromJson(json, Customers.class); Object custId = new JSONObject(json).get("cust_id"); if (custId instanceof String) { customers.setCustId((String) custId); else if (custId instanceof JSONArray) { customers.setCustId(convertToStringArray(new JSONArray(custId))); }

Answer4:

Refer <a href="https://stackoverflow.com/a/44968691/3488928" rel="nofollow">this</a>.

Now the problem is that you will have to write your own code on the returned map to get the desired result.

Recommend

  • Best way to handle offline and online development with Git
  • Jest - Cannot find module 'setupDevtools' from 'setup.js'
  • How to prompt user that edits have been made upon changing pages or sorting in Kendo Grid
  • passing parameter to server in ExtJs
  • twisted.internet.error.ConnectError when run scrapy spider
  • Extjs, handling success or failure when doing a standard submit in a form
  • How to implement simple validation in Scala
  • Return to second to last URL in MVC (return View with previous filter conditions applied)?
  • crash in __tcf_0
  • several dataProvider per one Test in TestNG
  • For loop with if condition on multiple R functions
  • How to specify input and output paths from cmd.exe for a PowerShell script?
  • Trying to get the char code of ENTER key
  • Using Sax parsing to edit and write XML in VB6
  • What does 'Language neutral' mean with regard to MAKELANGID?
  • Assign variable to the value in HTML
  • Debug.DrawLine not showing in the GameView
  • How to use carriage return with multiple line?
  • azure media services - The request body is too large and exceeds the maximum permissible limit
  • Content-Length header not returned from Pylons response
  • Yii2: Config params vs. const/define
  • Unity3D & Android: Difference between “UnityMain” and “main” threads?
  • How to create a file in java without a extension
  • Django rest serializer Breaks when data exists
  • OpenGL ES texture problem, 4 duplicate columns and horizontal lines (Android)
  • Ajax Loaded meta Tags
  • How to rebase a series of branches?
  • Xamarin Forms - UWP Fonts
  • Azure Cloud Service Web Role web pages do not load
  • Cross-Platform Protobuf Serialization
  • DirectX11 ClearRenderTargetViewback with transparent buffer?
  • Perl system calls when running as another user using sudo
  • Arrow is showed instead of the material design version hamburger icon. Why doesn't syncState in
  • When should I choose bucket sort over other sorting algorithms?
  • what is the difference between the asp.net mvc application and asp.net web application
  • Arrays break string types in Julia
  • How to format a variable of double type
  • PHP: When would you need the self:: keyword?
  • coudnt use logback because of log4j
  • JaxB to read class hierarchy