I'm trying to parse some json data that looks like this:
{
"store_name": "Coffee Co",
"location": "New York",
"supplier_name": "Cups Corps",
"supplier_id": 12312521,
"supplier_email": "cups#cups.net"
}
And here's my Java POJOs
class Store {
#com.google.gson.annotations.SerializedName("store_name")
String storeName;
String location;
Supplier supplier;
}
class Supplier {
String id;
String name;
String email;
}
//Getters and setters omitted
The issue I'm having is that the fields for the Supplier are flattened directly into the Store record. I tried adding a TypeAdapter to my Gson object for Supplier but it doesn't get triggered because there's no field named supplier on the incoming json object. I also can't use an alternate name for supplier because it needs information from all three of the fields in order to be created.
What is the best way to parse this data so that the nested Supplier field can also be populated?
What you could do is use a custom deserializer:
class StoreDeserializer implements JsonDeserializer<Store> {
#Override
public Store deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject jsonObject = jsonElement.getAsJsonObject();
Supplier supplier = new Supplier(
jsonObject.get("supplier_id").getAsInt(),
jsonObject.get("supplier_name").getAsString(),
jsonObject.get("supplier_email").getAsString()
);
return new Store(
jsonObject.get("store_name").getAsString(),
jsonObject.get("location").getAsString(),
supplier
);
}
}
You can then deserialize by registering the deserializer:
String json = "{\"store_name\":\"Coffee Co\",\"location\":\"New York\",\"supplier_name\":\"Cups Corps\",\"supplier_id\":12312521,\"supplier_email\":\"cups#cups.net\"}";
Gson gson = new GsonBuilder().registerTypeAdapter(Store.class, new StoreDeserializer()).create();
Store store = gson.fromJson(json, Store.class);
Note that I changed the type of Supplier#id to int, since it is numeric in your JSON:
class Supplier {
int id;
String name, email;
Supplier(int id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
class Store {
String storeName, location;
Supplier supplier;
Store(String storeName, String location, Supplier supplier) {
this.storeName = storeName;
this.location = location;
this.supplier = supplier;
}
}
Related
This question already has answers here:
multiple JsonProperty Name assigned to single property
(5 answers)
Closed 1 year ago.
I have an entity class with JsonProperty attribute as follows:
public class Book
{
[JsonProperty("id")]
public long BookId { get; set; }
[JsonProperty("name")]
public string BookName { get; set; }
}
I want to deserialize this from JSON string to the entity. Due to JsonProperty id & name, the json should have the id/name schema and in that case the deserialize works perfectly (as expected). But in some cases, the json string can also have BookId and BookName. Can I have multiple JsonProperty attributes to handle this? Or is there a way to let the serializer know, that if not id then deserialize using BookId? Or any other way to handle this use case?
You could solve this by defining alternative properties, which redirects to existing ones. For example,
public class Book
{
[JsonProperty("bookId")]
public long BookId { get; set; }
[JsonProperty("id")]
private long Id
{
get => BookId;
set => BookId = value;
}
[JsonProperty("bookName")]
public string BookName { get; set; }
[JsonProperty("name")]
private string Name
{
get => BookName;
set => BookName = value;
}
}
Demo
You can replace the property name before deserializing the json.
var book = #"
[
{
'id': 123456789,
'name': 'book1'
},
{
'BookId': 1234567,
'BookName': 'book2'
}
]
";
var after = book.Replace("BookId", "id").Replace("BookName", "name");
var sbook = JsonConvert.DeserializeObject<List<Book>>(after);
Then you can get the entire deserialization object.
I'm coding an API using Jhipster. Server side is programmed with Spring-Boot. I want to get JSON Object that i send with PostMan
{
"user" : {
"name" : "name",
"surname": "surname"
}
}
I create a ressource and a class to get this JSON Object
#RequestMapping(value = "/",method = RequestMethod.POST,produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public ResponseEntity<AlertBalance> create(#RequestBody User user) throws URISyntaxException {
System.out.println("name "+ user.getName()+"/ surname : "+User.getSurname());
}
User class Code
#JsonIgnoreProperties(ignoreUnknown = true)
public class User implements Serializable {
private String name;
private String surname ;
#JsonCreator
public User(#JsonProperty("surname") String surname , #JsonProperty("name") String name){
this.surname = surname;
this.name = name;
}
public User(){
}
//setters and getters
}
The create() method is called when I do request from postman, but the value of name and surname is null. Do you know what I can do to get the real values ?
I just find the mistake, the JSON Object I sent was incompatible. I change it with
{
"name" : "name",
"surname": "surname"
}
and now it works.
I am trying to deserialize this type of json
{
"_embedded": {
"list": [
{
"000000": {
"date": "2015-07-10 14:29:15"
}
},
{
"111111": {
"date": "2015-07-11 14:29:15"
}
}
]
}
}
I manage to get a list inside my embedded object but the list entries are empty.
My Embedded class looks like this
public class Embedded {
#SerializedName("list")
private List<ListEntry> entries;
}
But I am not able to deserialize the list's entries. I think the problem is the fact that the map is nested inside an object that doesn't have a name.
public class ListEntry {
private Map<String, ListEntryInfo> map;
}
The initial problem is the way you declared your hierarchy. A ListEntry is a Map<String, ListEntryInfo> but does not have a Map<String, ListEntryInfo>. To make it work you have three options:
declare the ListEntry class as class ListEntry extends HashMap<String, ListEntryInfo> {}, which is a bad idea in my opinion
get rid of the ListEntry class and declare the entries list like this #SerializedName("list") List<Map<String, ListEntryInfo>> entries;
use the approach I initially described below, by implementing a custom deserializer
As said before what you could do is to write a custom deserializer, so that you have a more typed list of entries.
As a ListEntry instance has only one ListEntryInfo value mapped to a key, I would change the structure to this:
class ListEntry {
private String key;
private ListEntryInfo value;
public ListEntry(String key, ListEntryInfo value) {
this.key = key;
this.value = value;
}
public String toString() {
return key + " -> " + value;
}
}
class ListEntryInfo {
//assuming we store the date as a String for simplicity
#SerializedName("date")
private String date;
public ListEntryInfo(String date) {
this.date = date;
}
public String toString() {
return date;
}
}
Now you need to write a deserializer to create a new ListEntry instance when you deserialize the JSON:
class ListEntryDeserializer implements JsonDeserializer<ListEntry> {
#Override
public ListEntry deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Iterator<Map.Entry<String, JsonElement>> ite = json.getAsJsonObject().entrySet().iterator();
//you may want to throw a custom exception or return an "empty" instance there
Map.Entry<String, JsonElement> entry = ite.next();
return new ListEntry(entry.getKey(), context.deserialize(entry.getValue(), ListEntryInfo.class));
}
}
This deserializer will read each ListEntry. As it's composed of a single key-value pair (in the first case the String "000000" is mapped to one ListEntryInfo and so on), we fetch the key and deserialize the corresponding ListEntryInfo with JsonDeserializationContext instance.
The final step, is to register it within the parser:
Gson gson = new GsonBuilder().registerTypeAdapter(ListEntry.class, new ListEntryDeserializer()).create();
Running it on your example, you should get:
[000000 -> 2015-07-10 14:29:15, 111111 -> 2015-07-11 14:29:15]
I have a JSON document similar to the following:
{
"aaa": [
{
"value": "ewfwefew"
}
],
"bbb": [
{
"value": "ewfewfe"
}
]
}
I need to deserialize this into something more clean such as:
public class MyEntity{
private String aaa;
private String bbb;
}
What's the best way to unwrap each array and extract the "value" field on deserialization?
#Tim Mac's response is correct, but you can make it more elegant by writing a custom deserializer for your MyEntity class.
It should be something like this:
private class MyEntityDeserializer implements JsonDeserializer<MyEntity> {
public MyEntity deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject rootObj = json.getAsJsonObject();
String nid = rootObj
.get("nid")
.getAsJsonArray()
.get(0)
.getAsJsonObject()
.get("value")
.getAsString();
String uuid = rootObj
.get("uuid")
.getAsJsonArray()
.get(0)
.getAsJsonObject()
.get("value")
.getAsString();
MyEntity entity = new MyEntity(nid, uuid);
return entity;
}
}
Then you have to register the TypeAdapter with:
Gson gson = new GsonBuilder().registerTypeAdapter(MyEntity.class, new MyEntityDeserializer()).create();
And finally you just have to parse your JSON as usual, with:
MyEntity entity = gson.fromJson(yourJsonString, MyEntity.class);
Gson will automatically use your custom deserializer to parse your JSON into your MyEntity class.
If you can't change the json that you're getting, you might consider deserializing it the way it is, and then converting it to something more manageable?
public class TmpEntity {
public Value[] nid {get;set;}
public Value[] uuid {get;set;}
}
public class Value {
public string value {get;set;}
}
public class MyEntity {
public string nid {get;set;}
public string uuid {get;set;}
}
var tmp = ...; //deserialize using javascriptserializer
var converted = tmp.Select(a => new MyEntity()
{
nid = a.nid.First().value,
uuid = a.uuid.First().value
}
I have the following classes -
Employee
public class Employee {
private String firstName ;
private String lastName ;
private String emailAddress ;
private String ssn ;
}
Payroll
public class Payroll {
// different payroll related fields
private Employee emp ;
}
HR
public class HR {
// different HR related fields
private Employee emp ;
}
Now when I serialize my Payroll class, I don't want to serialize my ssn field from Employee class.
Where as when I serialize my HR class, I don't want to serialize my emailAddress field from Employee class.
How I can dynamically exclude fields from serializing by using Jackson JSON API?
How I can dynamically exclude fields from serializing by using Jackson JSON API?
This seems like a prime candidate for applying JacksonJsonViews.
public class Employee {
private String firstName;
private String lastName;
#JsonView(Views.Payroll.class) private String emailAddress;
#JsonView(Views.HR.class) private String ssn;
}
public class Payroll {
// snip
#JsonView(Views.Payroll.class)
private Employee emp;
}
public class HR {
// snip
#JsonView(Views.HR.class)
private Employee emp;
}
You can achieve this with the help of a JsonFilter.
Annotate the class you want to filter as thus:
#JsonFilter("employeeFilter")
public class Employee {
private String firstName ;
private String lastName ;
private String emailAddress ;
private String ssn ;
}
Create a service called FilterBeanService(Or anything really)
#Service
public class FilterBeanService {
// fields is an array of field names you wish not to sned in your response
// beanFilterName is value you give when you annotage your bean class
// dataSet is the data you want to filter
public static MappingJacksonValue filterBean(String[] fields, String beanFilterName, Object dataSet ) {
SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.serializeAllExcept(fields);
FilterProvider filterProvider = new SimpleFilterProvider().addFilter(beanFilterName, filter);
MappingJacksonValue jacksonValue = new MappingJacksonValue(dataSet);
jacksonValue.setFilters(filterProvider);
return jacksonValue;
}
}
In your controller, you can then filter fileds you do not want
#GetMapping("/whatever")
public ResponseEntity<MappingJacksonValue> getSomething(){
List<Employee> employees = eventRepo.findAll();
String[] fields = {"ssn"};
MappingJacksonValue jacksonValue = FilterBeanService.filterBean(fields, "employeeFilter", employees);
return jacksonValue;
}
I answered below to dynamically deserialise with customer reader. Can do similar thing with writer to ignore when serialising.
Jackson Change JsonIgnore Dynamically