Deserialization issue while linking two ArangoDB collection in ArrayList - java

I have two db collections Agency & Program where an Agency can have many programs and all the further concept implementation is using Programs only. So I have created two POJO
public class Agency implements Serializable {
#DocumentField(DocumentField.Type.ID)
private String agencyId;
#DocumentField(DocumentField.Type.KEY)
#SerializedName("AGENCYNAME")
private String agencyName;
#SerializedName("SHORTNAME")
private String shortName;
#Expose(serialize = false, deserialize = true)
#SerializedName("PROGRAMS")
private List<Program> programs;
// Other fields with Getter & Setters
}
public class Program implements Serializable {
#DocumentField(DocumentField.Type.ID)
private String programId;
#SerializedName("PROGRAMNAME")
private String programName;
#DocumentField(DocumentField.Type.KEY)
#SerializedName("SHORTNAME")
private String shortName;
#SerializedName("AGENCY")
private Agency agency;
// Other fields with Getter & Setters
}
When I run AQL : for a in AGENCY return merge(a, {PROGRAMS: (for p in PROGRAMS FILTER p.AGENCY == a._id return p)})
I get following JSON
[
{
"AGENCYNAME": "Dummy Agency 1",
"SHORTNAME": "DA1",
"_id": "AGENCY/1062620",
"_key": "1062620",
"_rev": "_URnzj-C---",
"PROGRAMS": [
{
"_key": "DA1DP1",
"_id": "PROGRAMS/DA1DP1",
"_rev": "_UQ6dGOG---",
"AGENCY": "AGENCY/1062620",
"PROGRAMNAME": "DA1 Dummy Program 1",
"SHORTNAME": "DA1DP1"
}
]
},
{
"AGENCYNAME": "Dummy Agency 2",
"SHORTNAME": "DA2",
"_id": "AGENCY/1062358",
"_key": "1062358",
"_rev": "_URnzj-C---",
"PROGRAMS": [
{
"_key": "DA2DP1",
"_id": "PROGRAMS/DA2DP1",
"_rev": "_UQ6dGOG---",
"AGENCY": "AGENCY/1062358",
"PROGRAMNAME": "DA2 Dummy Program 1",
"SHORTNAME": "DA2DP1"
}
]
}
]
When I run this query from arangodb-java-driver 4.1 it throws an exception while deserialization
com.arangodb.velocypack.exception.VPackValueTypeException: Expecting type OBJECT
and if I comment these lines from Agency.java it works fine, But I need to have List in agency.
#Expose(serialize = false, deserialize = true)
#SerializedName("PROGRAMS")
private List<Program> programs;
Is there a way to handle lists in such cases ?
Please suggest a way to overcome this..
Thanks :)

in your POJO Program you have a field agency which is from type Agency but in the JSON you get from the DB this field is from type string. Change type Agency to String and your code works.
update:
To get the result from your query which fits into your current java bean, run:
for a in AGENCY return merge(a, {PROGRAMS: (for p in PROGRAMS FILTER p.AGENCY == a._id return merge(p, {AGENCY: a}))})

Related

Get nested "results" list from OData V2 entity

To explain my issue, let's say that I'm retrieving the following OData V2 Entity:
{
"d": {
"EmployeeID": 1,
"LastName": "Davolio",
"FirstName": "Nancy",
"Orders": {
"results": [
{
"OrderID": 10258
}
]
},
"Territories": {
"results": [
{
"TerritoryID": "06897"
}
]
}
}
}
And I have the corresponding model Class:
#JsonRootName(value = "d")
public class Employee {
#JsonProperty("EmployeeID")
private int employeeId;
#JsonProperty("LastName")
private String lastName;
#JsonProperty("FirstName")
private String firstName;
#JsonProperty("Orders")
private List<Order> orders;
#JsonProperty("Territories")
private List<Territory> territories;
...
}
As expected the exception com.fasterxml.jackson.databind.exc.MismatchedInputException is being triggered because the "Orders" and "Territories" collections are actually within the property "results", as the OData V2 states.
Do you guys have any idea how to ignore the nested "results" property and get the lists straight away?
Is a custom deserializer or wrapper class really needed in this case?
Thanks!
There are always multiple ways to tackle this problem.
One is to create a wrapper class and have Employee Directly reference it.
For example:
public class WrapperDto<T> implements Serializable {
#JsonProperty("results")
private List<T> elements;
}
public class Employee {
...
#JsonProperty("Orders")
private WrapperDto<Order> orders;
#JsonProperty("Territories")
private WrapperDto<Territory> territories;
...
}
when you want to get the orders, you have to call orders.getResults() to get the List
another solution can be found here where you have a custom wrapper:
How to map a nested value to a property using Jackson annotations?

How can I produce a JSON_OBJECT instead of JSON_ARRAY as result to my API queries

For some time I have Struggled with this problem
I have distilled a short sample to produce my results
When Spring-Boot produces api output and there is more than one record it is enclosed as a JSON_ARRAY starting with [ and ending with ] with comma-seperated JSON_OBJECTS inside
I Want the result to be surrounded by another JSON_OBJECT like
{entity:[{.....},{.....}]}
e.g.
[
{
"locationId": "l1",
"locationName": "New York"
},
{
"locationId": "l3",
"locationName": "London"
}
]
must become
{
location:{
[{
"locationId": "l1",
"locationName": "New York"
},
{
"locationId": "l3",
"locationName": "London"
}]
}}
public class Location {
private String id;
private String name;
}
// Getter & Setters etc ommited
Obvously I have googled for this and have tried many solution and could not find any
If I add "org.springframework.boot:spring-boot-starter-data-rest"
to my project spring will create some endpoints for my entities that actually produces the desired results, however I have some non-standars queries that is excluded and I need to do them myself
If you can teach me to overcome this difficulty I will apreciate it
Create a class as follows:
public class LocationWrapper {
private List<Location> location;
}
and return an instance of LocationWrapper from the #RestController method.

Unmarshalling an array is not giving me any values

When I unmarshal my JSON, the Warehouses instance is ok with however many warehouse instances are in it's list.
Each warehouse instance has the url field but the WarehouseField list has one instance with blank values.
I'm not sure what I'm missing.
JSON
{
"warehouses": [
{
"warehouse": {
"PRiyA": "0",
"WHID": "1 ALABO",
"PRixA": ""
},
"url": "http://ACL-HPDV6:8080/HSyncREST/api/v1/warehouses/PLL/1 ALABO"
},
{
"warehouse": {
"PRiyA": "0",
"WHID": "1000 EDWAR",
"PRixA": ""
},
"url": "http://ACL-HPDV6:8080/HSyncREST/api/v1/warehouses/PLL/1000 EDWAR"
},
],
"url": "http://ACL-HPDV6:8080/HSyncREST/api/v1/warehouses/PLL",
"status": " "
}
Code used to unmarshall
public static void main(String[] args) throws Exception {
Class<?>[] ctx = {Warehouses.class, Warehouse.class, WarehouseField.class};
JAXBContext jc = JAXBContext.newInstance(ctx);
Unmarshaller um = jc.createUnmarshaller();
um.setProperty(UnmarshallerProperties.MEDIA_TYPE, "application/json");
um.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
Source json = new StreamSource(new File("D:/warehouses.json"));
Warehouses warehouses = um.unmarshal(json, Warehouses.class).getValue();
Model classes
public class Warehouses {
public List<Warehouse> warehouses;
public String url;
public String status;
<getters and setters>
}
public class Warehouse {
public List<WarehouseField> warehouse;
public String url;
<getters and setters>
}
public class WarehouseField {
#XmlAttribute
public String implName;
#XmlValue
public String value;
<getters and setters>
}
First of all I suggest you make all fields private, you have getters & setters for your fields.
It is a also a good idea to separate the response(?) DTO class name from the field and actual type naming.
Assuming that field names in the response DTOs tell the actual type, then do a bit refactoring like Warehouses to WarehousesResponse and Warehouse to WarehouseResponse.
Then about the "array", clip from the JSON:
"warehouse": {
"PRiyA": "0",
"WHID": "1 ALABO",
"PRixA": ""
}
this is not an array named warehouse so it not deserializing to a List nicely.
It is an Object of type Warehouse (that is why distinction WarehouseResponse, for clarity but see also mention about Map later) that is a field named warehouse in Object of type WarehouseResponse (assuming you agree on naming policy).
One option is to create a class like:
#Getter #Setter
public class Warehouse {
private String PRiyA;
private String WHID;
private String PRixA;
}
and change WarehouseResponse like:
#Getter #Setter
public class WarehouseResponse {
// Change the list to warehouse object as it is in response
// private List<WarehouseField> warehouse;
private Warehouse warehouse;
private String url;
private Date date = new Date();
}
Usually it is also possible to set key/value-pairs simply - for an example - to a Map<String,String> so in this case WarehouseResponses could also have private HashMap<String, String> warehouse and no class Warehouse would be needed. However I could not get it working with my Moxy knowledge.
So I presented how you can deserialize (and serialize) the format you gave in your JSON but I can not know it this then suits your possible XML needs

Why do I not get any results from MongoOperation when querying for an entity?

When I execute request to db
db.users.find({"name": "Max"})
I get this result
{"_id":ObjectId("5785718ee271a7c7ebaad28b"),"name":"Max","visits-by-day":[{"day":"Thursday","visitsAmount":20},{"day":"Saturday","visitsAmount":4}]}
JSON structure example:
{
"users": [
{
"name": "Bobby",
"visits-by-day": [
{
"day": "Sunday",
"visitsAmount": 8
},
{
"day": "Monday",
"visitsAmount": 3
}
]
}
]
}
Here my Java code
MongoUser user = mongoTemplate.findOne(query(where("name").is("Max")), MongoUser.class);
The model
#Document
public class MongoUser {
#Id
private String id;
private String name;
private List<VisitsPerDay> visitsByDay;
// Getters & Setters omitted
}
public class VisitsPerDay {
private String day;
private Integer visitsAmount;
// Getters & Setters omitted
}
Why Spring does return a null empty instead of serialized Java object?
By default, the collection queried for a given typed is derived from the simple name of the domain type you want to read. In your case, that would be mongoUser. To get your example to work, you basically have two options:
Explicitly configure the collectionName in the #Document annotation on MongoUser to users. That will basically tie instances of that class to that collection and let all data access operations for that class to work with that collection (e.g. for repositories etc.).
When calling MongoTemplate, use the overload of findOne(…) that takes an explicit collection name:
template.findOne(query(…), MongoUser.class, "users");

How to make an advanced search with Spring Data REST?

My task is to make an advanced search with Spring Data REST.
How can I implement it?
I managed to make a method to do a simple search, like this one:
public interface ExampleRepository extends CrudRepository<Example, UUID>{
#RestResource(path="searchByName", rel="searchByName")
Example findByExampleName(#Param("example") String exampleName);
}
This example works perfectly if I have to go simply to the url:
.../api/examples/search/searchByName?example=myExample
But what I have to do if there are more than one field to search?
For example, if my Example class has 5 fields, what implementation should I have to make an advanced search with all possibiles fileds?
Consider this one:
.../api/examples/search/searchByName?filed1=value1&field2=value2&field4=value4
and this one:
.../api/examples/search/searchByName?filed1=value1&field3=value3
What I have to do to implement this search in appropriate way?
Thanks.
Spring Data Rest has integrated QueryDSL with web support as well which you can use for your advanced search requirement. You need to change your repository to implement QueryDslPredicateExecutor and things will work out of the box.
Here is a sample from the blog article about the feature:
$ http :8080/api/stores?address.city=York
{
"_embedded": {
"stores": [
{
"_links": {
…
},
"address": {
"city": "New York",
"location": { "x": -73.938421, "y": 40.851 },
"street": "803 W 181st St",
"zip": "10033-4516"
},
"name": "Washington Hgts/181st St"
},
{
"_links": {
…
},
"address": {
"city": "New York",
"location": { "x": -73.939822, "y": 40.84135 },
"street": "4001 Broadway",
"zip": "10032-1508"
},
"name": "168th & Broadway"
},
…
]
},
"_links": {
…
},
"page": {
"number": 0,
"size": 20,
"totalElements": 209,
"totalPages": 11
}
}
I managed to implement this using Query by Example.
Let's say you have the following models:
#Entity
public class Company {
#Id
#GeneratedValue
Long id;
String name;
String address;
#ManyToOne
Department department;
}
#Entity
public class Department {
#Id
#GeneratedValue
Long id;
String name;
}
And the repository:
#RepositoryRestResource
public interface CompanyRepository extends JpaRepository<Company, Long> {
}
(Note that JpaRepository implements QueryByExampleExecutor).
Now you implement a custom controller:
#RepositoryRestController
#RequiredArgsConstructor
public class CompanyCustomController {
private final CompanyRepository repository;
#GetMapping("/companies/filter")
public ResponseEntity<?> filter(
Company company,
Pageable page,
PagedResourcesAssembler assembler,
PersistentEntityResourceAssembler entityAssembler
){
ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnoreCase()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING);
Example example = Example.of(company, matcher);
Page<?> result = this.repository.findAll(example, page);
return ResponseEntity.ok(assembler.toResource(result, entityAssembler));
}
}
And then you can make queries like:
localhost:8080/companies/filter?name=google&address=NY
You can even query nested entities like:
localhost:8080/companies/filter?name=google&department.name=finances
I omitted some details for brevity, but I created a working example on Github.
The implementation of query methods is widely documented in Spring reference documentation and tons of technical blogs, though quite a bunch are outdated.
Since your question is probably "How can I perform a multi-parameter search with any combination of fields without declaring an awful lot of findBy* methods?", the answer is Querydsl, which is supported by Spring.
I have found a working solution for this task.
#RepositoryRestResource(excerptProjection=MyProjection.class)
public interface MyRepository extends Repository<Entity, UUID> {
#Query("select e from Entity e "
+ "where (:field1='' or e.field1=:field1) "
+ "and (:field2='' or e.field2=:field2) "
// ...
+ "and (:fieldN='' or e.fieldN=:fieldN)"
Page<Entity> advancedSearch(#Param("field1") String field1,
#Param("field2") String field2,
#Param("fieldN") String fieldN,
Pageable page);
}
With this solution, using this base url:
http://localhost:8080/api/examples/search/advancedSearch
We can make advanced searches with all the fields that we need.
Some examples:
http://localhost:8080/api/examples/search/advancedSearch?field1=example
// filters only for the field1 valorized to "example"
http://localhost:8080/api/examples/search/advancedSearch?field1=name&field2=surname
// filters for all records with field1 valorized to "name" and with field2 valorized to "surname"
I guess You can try following:
List<Person> findDistinctPeopleByLastnameOrFirstname(#Param("lastName")String lastname, #Param("firstName")String firstname);
and examples/search/searchByLastnameOrFirstname?firstName=value1&lastName=value2
Check out: http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation

Categories

Resources