Convert ObjectNode to Java Object using Jackson - java

I have a json:
{
"response": {
"GeoObjectCollection": {
"featureMember": [
{
"GeoObject": {
"description": "Country",
"name": "City",
"Point": {
"pos": "31.992615 45.057626"
}
}
},
{
"GeoObject": {
"description": "Country",
"name": "City",
"Point": {
"pos": "49.242414 49.895935"
}
}
}
]
}
}
}
I created DTO:
GeographicCoordinateDto.java:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class GeographicCoordinateDto {
#JsonProperty("description")
private String location;
#JsonProperty("name")
private String cityName;
#JsonProperty("Point")
private GeographicCoordinatesDto geoCoordinates;
}
GeographicCoordinatesDto.java:
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class GeographicCoordinatesDto {
#JsonProperty("pos")
private String geoCoordinates;
}
Then I get JsonNode:
List<JsonNode> responseArrayOfObjects = mapper.readValue(new URL(yandexGeoCoderRestUrl+address), ObjectNode.class).findValues("GeoObject");
And I'm trying to convert to my DTO:
GeographicCoordinatesDto geo = mapper.convertValue(responseArrayOfObjects.get(0), GeographicCoordinatesDto.class);
But, I've null object:
GeographicCoordinatesDto(geoCoordinates=null)
What could be wrong?
UPDATE:
responseArrayOfObjects contains:

You are trying to get pos from the GeographicCoordinatesDto object, but it is inside the Point object of GeographicCoordinatesDto.
You can do this instead:
List<JsonNode> responseArrayOfObjects = mapper.readValue(new URL(yandexGeoCoderRestUrl+address), ObjectNode.class).findValues("Point");
or create another class for Point:
#JsonIgnoreProperties(ignoreUnknown = true)
class Point {
#JsonProperty("pos")
private String geoCoordinates;
}
and use it in GeographicCoordinatesDto:
#JsonIgnoreProperties(ignoreUnknown = true)
class GeographicCoordinatesDto {
#JsonProperty("Point")
private Point point;
}

Related

#JsonView does not filter fields

I have problem while deserializing JSON to Java Object. I have models and views as shown below:
public class View {
public static class CreateIpSuccessResponse {}
public static class CreateIpErrorResponse {}
}
I use this views in this classes:
Root class:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CreateIpResponse {
#JsonProperty(value = "pResponseCode")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private Object pResponseCode;
#JsonProperty(value = "pResponse")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private CreateIpPResponse createIpPResponse;
}
First subclass:
#Data
public class CreateIpPResponse {
#JsonProperty("Status")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private String status;
#JsonProperty("Result")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private CreateIpResult result;
#JsonProperty("responseCode")
#JsonView({View.CreateIpSuccessResponse.class, View.CreateIpErrorResponse.class})
private String responseCode;
}
Second subclass:
#Data
public class CreateIpResult {
#JsonProperty(value = "partyid")
#JsonView(View.CreateIpSuccessResponse.class)
private String partyId;
#JsonProperty(value = "Error")
#JsonView(View.CreateIpErrorResponse.class)
private String error;
}
Example of my json deserialization:
public CreateIpResponse createIp(CreateIpRequest createIpRequest) throws IOException, SQLException {
String pRequest = new ObjectMapper().writer().withDefaultPrettyPrinter().writeValueAsString(createIpRequest);
Map<String, Object> response = openAccountRepository.callProcedure(pRequest, "createClientIP");
BigDecimal responseCode = (BigDecimal) response.get("pResponseCode");
if (responseCode.equals(new BigDecimal("200"))) {
return mapper
.readerWithView(View.CreateIpSuccessResponse.class)
.forType(CreateIpResponse.class)
.readValue(mapper.writeValueAsString(response));
} else {
return mapper
.readerWithView(View.CreateIpErrorResponse.class)
.forType(CreateIpResponse.class)
.readValue(mapper.writeValueAsString(response));
}
}
When I deserialize CreateIpSuccessResponse view, I expect:
{
"pResponseCode": 200,
"pResponse": {
"Status": "OK",
"Result": {
"partyid": "98493305"
},
"responseCode": "200"
}
}
But I get:
{
"pResponseCode": 200,
"pResponse": {
"Status": "OK",
"Result": {
"partyid": "98493305",
"Error": null
},
"responseCode": "200"
}
}
and vice versa, when I deserialize CreateIpErrorResponse view, I expect:
{
"pResponseCode": 400,
"pResponse": {
"Status": "Error",
"Result": {
"Error": "Некорректная дата выпуска"
},
"responseCode": "200"
}
}
But I get:
{
"pResponseCode": 400,
"pResponse": {
"Status": "Error",
"Result": {
"partyid": null,
"Error": "Некорректная дата выпуска"
},
"responseCode": "200"
}
}
My question is why i don`t getting result that i need?
It seems that ObjectMapper is not ignoring null values when serializing objects. So when creating he object mapper use the setSerializationInclusion(Include.NON_NULL) method call to tell the ObjectMapper to ignore the null values:
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
You can find the details here
Edit: added the summarization of the solution as moken suggested in comments.

Java mapping JSON with POJO using jackson

I want to create this JSON using jakson annotated POJOS. The issue I have when I create a new class without #JsonProperty annotation to represent the last {"id":"123ccc","role":"dddd"}, it by default take the class name and create something like "customer":{"id": "123ccc","role":"dddd"}.
The JSON Structure I indent to build
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
{
"id": "123ccc",
"role": "dddd"
}
]
}
The JSON I'm receiving from the below code.
{
"relatedParty": [
{
"contact": [
{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
},
{
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}
],
"role": "ccc",
"fullName": "ccc"
},
"customer" : {
"id": "123ccc",
"role": "dddd"
}
]
}
What would be a workaround to get the exact JSON format as the image. Current Implementation is below.
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public class RelatedParty {
#JsonProperty(value = "contact")
private List<Contact> contact;
#JsonProperty(value = "role")
private String role;
#JsonProperty(value = "fullName")
private String fullName;
private Customer customer;
public List<Contact> getContact() {
return contact;
}
public void setContact(List<Contact> contact) {
this.contact = contact;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
public class Customer {
#JsonProperty(value = "id")
private String id;
#JsonProperty(value = "role")
private String role;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
}
You need to create additional and different POJO classes to model your JSON correctly. Basically, JSON arrays will be handle in Java lists, and JSON objects will be handled in Java classes.
Starting from the inside (most nested level) of the JSON, and working our way out:
NOTE: getters and setters not shown here
Characteristic.java
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Characteristic {
#JsonProperty("city")
private String city;
#JsonProperty("country")
private String country;
#JsonProperty("emailAddress")
private String emailAddress;
}
Contact.java (contains our characteristics):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Contact {
#JsonProperty("mediumType")
private String mediumType;
#JsonProperty("characteristic")
private Characteristic characteristic;
}
The above two classes handle the innermost objects. If we remove them from your target JSON, that leaves the following:
{
"relatedParty": [{
"contact": [...],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}
Note that the contact field is a JSON array, not an object - so we do not create a Java Contact class (which would be for a JSON object).
To handle the above I create two more classes:
RelatedPartyInner.java (contains a list of contacts)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty_ {
#JsonProperty("contact")
private List<Contact> contact = null;
#JsonProperty("role")
private String role;
#JsonProperty("fullName")
private String fullName;
#JsonProperty("id")
private String id;
}
RelatedParty.java (wraps everything in an outer object):
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RelatedParty {
#JsonProperty("relatedParty")
private List<RelatedPartyInner> relatedParty = null;
}
To test this I create the following data:
Characteristic chr1 = new Characteristic();
chr1.setCity("xxx");
chr1.setCountry("xxx");
Characteristic chr2 = new Characteristic();
chr2.setEmailAddress("yyy#yy.yyy");
Contact con1 = new Contact();
con1.setMediumType("xxx");
con1.setCharacteristic(chr1);
Contact con2 = new Contact();
con2.setMediumType("yyy");
con2.setCharacteristic(chr2);
List<Contact> cons = new ArrayList<>();
cons.add(con1);
cons.add(con2);
RelatedPartyInner rpi1 = new RelatedPartyInner();
rpi1.setContact(cons);
rpi1.setRole("ccc");
rpi1.setFullName("ccc");
RelatedPartyInner rpi2 = new RelatedPartyInner();
rpi2.setId("123ccc");
rpi2.setRole("dddd");
List<RelatedPartyInner> rpis = new ArrayList<>();
rpis.add(rpi1);
rpis.add(rpi2);
RelatedParty rp = new RelatedParty();
rp.setRelatedParty(rpis);
Finally, we can generate the JSON:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.writeValue(new File("rp.json"), rp);
The resulting file contains the following:
{
"relatedParty": [{
"contact": [{
"mediumType": "xxx",
"characteristic": {
"city": "xxx",
"country": "xxx"
}
}, {
"mediumType": "yyy",
"characteristic": {
"emailAddress": "yyy#yy.yyy"
}
}],
"role": "ccc",
"fullName": "ccc"
}, {
"role": "dddd",
"id": "123ccc"
}]
}

Returning a NodeEntity from neo4j doesn't contain relationships or connected nodes

I'm setting up a P.O.C. using Neo4j, and technically have everything I need working but would like it set up properly.
As a quick overview - I can create nodes and relationships, and traverse the graph (i.e. return all features available in a specific market) so I know these nodes/relationships have been created.
However, when I query to simply return a Node based on ID, it returns ONLY the data for that node - and not any relationships or connected nodes, for example, the markets its available in.
I've looked various places online that have not only a Node returned but also the subsequent nodes - though I follow what they're doing I cant seem to get it to work with mine.
Feature Repository:
#Repository
public interface FeatureRepository<T extends Feature> extends Neo4jRepository<T, Long> {
...
}
Colour Repository:
#Repository
public interface ColourRepository extends FeatureRepository<Colour>{
#Query("CREATE(feat:Colour:Feature {marketingDesc:{marketing}, engineeringDesc:{engineering}, code:{code}})")
Colour createColour(#Param("marketing") String marketingDesc, #Param("engineering") String engineeringDesc, #Param("code") String code);
#Query("MATCH (c:Colour {code:{colourCode}}) MATCH (c)-[:AVAILABLE_IN]->(market) RETURN c AS colour, COLLECT(market) AS markets")
Colour getColourByCode(#Param("colourCode") String colourCode);
Colour findByCode(#Param("code") String code);
}
Feature Entity:
#NodeEntity(label = "Feature")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Feature {
#Id
#GeneratedValue
private Long id;
private String marketingDesc;
private String engineeringDesc;
#Index(unique = true)
private String code;
#Relationship(type = "HAS_OPTION", direction = Relationship.INCOMING)
private List<Option> options = new ArrayList<>();
#Relationship(type = "AVAILABLE_IN")
private List<Market> markets = new ArrayList<>();
#Relationship(type = "HAS_PREREQUISITE", direction = Relationship.UNDIRECTED)
private List<Prerequisite> prerequisites = new ArrayList<>();
}
Colour Entity:
#AllArgsConstructor
#NodeEntity(label = "Colour")
public class Colour extends Feature {
}
Market Entity:
#NodeEntity(label = "Market")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Market {
#Id
#GeneratedValue
private Long id;
#Index(unique = true)
private String code;
private String market;
#Relationship(type = "AVAILABLE_IN", direction = Relationship.INCOMING)
private List<Option> features = new ArrayList<>();
}
Relationship Entity (for features to be connected to markets they can be bought in):
#RelationshipEntity(type = "AVAILABLE_IN")
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class Available {
#Id
#GeneratedValue
private Long Id;
private List<String> availableIn = new ArrayList<>();
#StartNode
private Feature feature;
#EndNode
private Market market;
}
Controller:
#RestController
public class ConfigController {
private final Handler configHandler;
public ConfigController(Handler configHandler) {
this.configHandler = configHandler;
}
#PostMapping(path = "/create/colour", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
public SimpleResponse createColour(#RequestBody Colour request) {
ColourService service = new ColourService(configHandler);
Colour created = service.createColour(request);
return SimpleResponse.builder().result("Created:", created).build();
}
#PostMapping(path = "/create/market", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
public SimpleResponse createMarket(#RequestBody Market request) {
MarketService service = new MarketService(configHandler);
Market created = service.createMarket(request);
return SimpleResponse.builder().result("Created", created).build();
}
#PostMapping(path = "/create/relationship/availableIn", consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
public SimpleResponse createAvailableInRelationship(#RequestBody OptionAvailInRequest request){
RelationshipService service = new RelationshipService(configHandler);
Object result = service.createAvailableInRelationship(request);
return SimpleResponse.builder().result("Result:", result).build();
}
#GetMapping(path = "/colour/{code}")
public SimpleResponse getColourByCode(#PathVariable(value = "code") String code) {
ColourService service = new ColourService(configHandler);
Colour colour = service.getColourByCode(code);
return SimpleResponse.builder().result("Colour:", colour).build();
}
#GetMapping(path = "/features/available/{mrktCode}")
public SimpleResponse getFeaturesInMarket(#PathVariable(value = "mrktCode") String mrktCode){
RelationshipService service = new RelationshipService(configHandler);
Collection<Feature> features = service.getFeaturesInMarket(mrktCode);
return SimpleResponse.builder().result("Features:", features).build();
}
}
Neo4jConfig file:
#Configuration
#EnableNeo4jRepositories(basePackages = "package.location")
#EnableTransactionManagement
public class Neo4jConfig {
#Bean
public org.neo4j.ogm.config.Configuration configuration() {
org.neo4j.ogm.config.Configuration configuration =
new org.neo4j.ogm.config.Configuration.Builder().build();
return configuration;
}
#Bean
public SessionFactory sessionFactory(org.neo4j.ogm.config.Configuration configuration) {
return new SessionFactory(configuration,"package.location");
}
#Bean
public Neo4jTransactionManager transactionManager(SessionFactory sessionFactory) {
return new Neo4jTransactionManager(sessionFactory);
}
}
So, for example, here I can create a Colour Node:
Example value:
{
"code": "string",
"engineeringDesc": "string",
"id": 0,
"marketingDesc": "string",
"markets": [
{
"code": "string",
"features": [
{}
],
"id": 0,
"market": "string"
}
],
"options": [
{}
],
"prerequisites": [
{}
]
}
What I send:
{
"code": "BLU",
"engineeringDesc": "Blue engineering",
"marketingDesc": "Blue marketing"
}
And this creates a Colour Node successfully:
{
"result": {
"Created:": {
"id": 0,
"marketingDesc": "Blue marketing",
"engineeringDesc": "Blue engineering",
"code": "BLU",
"options": [],
"markets": [],
"prerequisites": []
}
},
"error": null
}
I can create a Market Node:
Example Value:
{
"code": "string",
"features": [
{}
],
"id": 0,
"market": "string"
}
What I send:
{
"code": "UB",
"market": "England"
}
Which creates a Market Node successfully:
{
"result": {
"Created": {
"id": 1,
"code": "UB",
"market": "England",
"features": []
}
},
"error": null
}
I can then create a relationship between the two, to say that colour is available in that market:
{
"featureCode": "BLU",
"marketCode": "UB"
}
Which I can verify has been created by hitting:
localhost:8080/features/available/UB
{
"result": {
"Features:": [
{
"id": 0,
"marketingDesc": "Blue marketing",
"engineeringDesc": "Blue engineering",
"code": "BLU",
"options": [],
"markets": [],
"prerequisites": []
}
]
},
"error": null
}
However when I then go to return the Colour Node itself:
localhost:8080/colour/BLU
{
"result": {
"Colour:": {
"id": 0,
"marketingDesc": "Blue marketing",
"engineeringDesc": "Blue engineering",
"code": "BLU",
"options": [],
"markets": [],
"prerequisites": []
}
},
"error": null
}
The 'markets' option is always null. I have tried custom queries and building queries using the neo4j helper (e.g. findByCode etc.), and every example I can find will sucessfully return the related nodes, but I cant seem to get mine to.
Can anyone help?
P.S. Please let me know if there is anything else that would be helpful for you to see. Been trying to get this sorted for days....
Got the answer to this question...
Feature Entity should have been:
#Relationship(type = "AVAILABLE_IN")
#ApiModelProperty(hidden = true)
private Set<Available> markets = new HashSet<>();
Market Entity should have been:
#Relationship(type = "AVAILABLE_IN", direction = Relationship.INCOMING)
#ApiModelProperty(hidden = true)
private Set<Available> features = new HashSet<>();
Which gets the markets section of the feature JSON no longer null...
Now I have the problem that there's an infinite recursion loop between the two classes, with a feature displaying the markets and the markets displaying the features
EDIT:
For anyone else with this/similar issues, I've found a really good github resource.
GitHub neo4j ogm walkthrough
Helped a lot.

Spring RestTemplate returns null object when trying to deserialize nested list of object

So this is the Json I am trying to convert to a Java bean
I am using jackson to bind JSON to my data objects
{
"legend": {
"id": "379ec5d8c",
"name": "Stabil Koos",
"payers": [{
"id": "ab1651df-b835-495558-a2a5-2e6d42f7a41e",
"ranking": 1,
"buyer": {
"id": "67a6359838-0fda-43a6-9e2b-51a7d399b6a1",
"nationality": "en",
"stats": {
"gameeCount": 16581,
"count": 99098
}
}
},
{
"id": "379ecw555d8c",
"ranking": 2,
"buyer": {
"id": "2b451d0eda-ab0c-4345660-be3f-6ba3bebf1f81",
"nationality": "no",
"stats": {
"gamerCount": 1182,
"count": 7113
}
}
}
]
}
}
My beans look like this ;
public class League implements Serializable {
private String id;
private String name;
#JsonUnwrapped
private List<Payer> payers;
// getters and setters
Payers bean :
public class Payers implements Serializable {
private String id;
private long ranking;
private Buyer buyer;
// getters and setters
I am using Rest Template and postForObject in Junit
#Before
public void beforeTest() {
restTemplate = new RestTemplate();
headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
entity = new HttpEntity(REQUEST, headers);
}
And my final code to retrieve the object is :
#Test
public void retrieveData() {
League league = restTemplate.postForObject(ENDPOINT_URL, entity, League.class);
System.out.println(legend);
}
The JSON you show is for an object that has a league property which is a League object and not a league object itself. You need an additional response class:
class LeagueResponse {
private League league;
League getLeague() { return league; }
}
and:
LeagueResponse leagueResponse = restTemplate.postForObject(ENDPOINT_URL, entity, LeagueResponse.class);
League league = leagueResponse.getLeague();

Polymorphic deserialization jackson

I am trying to implement polymorphic deserialization in jackson and trying to make the same model work in two places.
I have ShopData object
public class ShopData extends Embeddable implements Serializable
{
private final int id;
private final String name;
private final String logoImageUrl;
private final String heroImageUrl;
public ShopData(#JsonProperty(value = "id", required = true) int id,
#JsonProperty(value = "name", required = true) String name,
#JsonProperty(value = "logoImageUrl", required = true) String logoImageUrl,
#JsonProperty(value = "heroImageUrl", required = true) String heroImageUrl)
{
this.id = id;
this.name = name;
this.logoImageUrl = logoImageUrl;
this.heroImageUrl = heroImageUrl;
}
}
My Embeddable object looks like this
#JsonTypeInfo(
use = JsonTypeInfo.Id.NAME,
include = JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({#JsonSubTypes.Type(value = AnotherObject.class, name = "AnotherObject"),
#JsonSubTypes.Type(value = ShopData.class, name = "shop")
})
public abstract class Embeddable
{
}
I am trying to make this model work in two places. This model works as expected.
public Order(#JsonProperty(value = "_embedded", required = true) Embeddable embedded)
{
this.embedded = (ShopData) embedded;
}
"_embedded": {
"shop": {
"id": 1,
"name": "",
"freshItems": 5,
"logoImageUrl": "",
"heroImageUrl": "",
"_links": {
"self": {
"href": "/shops/1"
}
}
}
While this doesn't
public ShopList(#JsonProperty(value = "entries", required = true) List<ShopData> entries)
{
this.entries = Collections.unmodifiableList(entries);
}
{
"entries": [
{
"id": 1,
"name": "",
"freshItems": 5,
"logoImageUrl": "",
"heroImageUrl": "",
"_links": {
"self": {
"href": "/shops/1"
}
}
}
]
}
And throws error :Could not resolve type id 'id' into a subtype
I understand the error but do not know how to resolve this. I would like to be able to use the same model in both cases. Is this possible?
Just found out the answer myself. Should have simply added this anotation
#JsonTypeInfo(use= JsonTypeInfo.Id.NONE)
public class ShopData extends Embeddable implements Serializable

Categories

Resources