I was new to the spring restful with Jackson and Hibernate OGM. I'm getting JSON format as below from web request
"Order":
{
"Orderdata": [ {"orderNo" : "1","location":"A"},{"orderNo" : "2","location":"B"},..]
}
Pojo class for order
class Order implements Serializable{
#ElementCollection
private List<OrderData> Orderdata = null;
//getter and setter
}
After inserting the document into MongoDB. I'm getting as below
"Order": {
"Orderdata": [
{
"Orderdata": {"orderNo" : "1","location":"A"}
},
{
"Orderdata": {"orderNo" : "2","location":"B"}
},
...
]}
Here I need to avoid the "Orderdata" name inside the each document created. Please help me to resolve this issue.
Related
I am trying to generate a JSON schema using POJOs with deep inheritance structure.
Using jackson-module-jsonSchema library I am able to generate a schema.
Given a simplified Java example:
public interface I {...}
public class A implements I {
public int varA;
}
public class B implements I {
public int varB;
}
public class C {
public I varC;
}
Below is my code to generate the schema:
import com.fasterxml.jackson.databind.*
import com.fasterxml.jackson.module.jsonSchema.*
// ...
ObjectMapper mapper = new ObjectMapper();
SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
mapper.acceptJsonFormatVisitor(mapper.constructType(C.class), visitor);
JsonSchema schema = visitor.finalSchema();
String outputSchemaJson = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(schema);
Actual Json Schema:
{
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:C",
"properties" : {
"varC" : {
"type" : "any"
}
}
}
Desired Json Schema:
{
"definitions": {
"A": {
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:A",
"properties" : {
"varA" : {
"type" : "integer"
}
}
},
"B": {
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:B",
"properties" : {
"varB" : {
"type" : "integer"
}
}
}
},
"type" : "object",
"id" : "urn:jsonschema:com:mycompany:GenerateSchemas:C",
"properties" : {
"varC" : {
"type" : "object",
"oneOf": [
{ "$ref": "urn:jsonschema:com:mycompany:GenerateSchemas:A" },
{ "$ref": "urn:jsonschema:com:mycompany:GenerateSchemas:B" }
]
}
}
}
I have tried overriding core classes from Json Schema library. This answer from stack overflow was helpful to generate a schema with references.
Now I am trying to understand what I need to override such that I can use reflection to get all inheriting-classes of an interface and add oneOf references to it.
I was finally able to figure out which classes I needed to override.
Notes:
Java does not support dynamically finding sub-classes via reflection through a simple out-of-box api. A workaround was to annotate classes with #JsonSubType which I was able to extract at run-time.
In version 2.9.8 of the json-module-schema library (where revision 1 of solution is written), there is no support yet for object definitions. Just to get the work done, I had to override a few extra classes to make this possible.
definitions need to be defined in the json schema only once at root level because there can be cases of recursive references.
With updated POJO code:
#JsonSubTypes({
#JsonSubTypes.Type(name = "A", value = A.class),
#JsonSubTypes.Type(name = "B", value = B.class)
})
public interface I {}
public class A implements I {
public int varA;
}
public class B implements I {
public int varB;
}
public class C {
public I varC;
}
The desired json schema output is produced successfully.
Given the following code: https://gist.github.com/rrmistry/2246c959d1c9cc45894ecf55305c61fd, I imported GenerateSchema class to make schema generation code more simplified:
public void generate() throws Exception {
generateSchemasFromJavaSubTypes(C.class);
}
private void generateSchemasFromJavaSubTypes(Class<?> classToGenerate) throws Exception {
JsonSchema schema = GenerateSchemas.generateSchemaFromJavaClass(classToGenerate);
ObjectMapper mapper = new ObjectMapper();
String jsonSchemaStr = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(schema);
}
GitHub issue has been created to request native support: https://github.com/FasterXML/jackson-module-jsonSchema/issues/135
I've a parent DAO:
#XmlRootElement//(name="metadata")
public class FolderAttributes {
private Map nameValueForListValue;
Child DAO:
#XmlAccessorType(XmlAccessType.FIELD)
public class ListWrapper {
#XmlElementWrapper(name = "attrValue")
private List<Object> list;
JSON request that works (if I use "metadata" name as root element):
"metadata": {
"nameValueForListValue": {
"signed": {
"attrValue": [
"ahsdfhgakjf"
]
},
"id": {
"attrValue": [
"12345678",
"87654321"
]
},
.......... continues
I don't want the tag "nameValueForListValue" in request, instead it should be smart enough to read rest of the values without that tag. Looks like it always needs to have the param name "nameValueForListValue" on the request. Is there any annotations that will do my job easier? I'm using Java 6 & jackson 1.9.
What about using #JsonAnySetter Jackson annotation
It would be something like:
#XmlRootElement//(name="metadata")
public class FolderAttributes {
private Map nameValueForListValue;
#JsonAnySetter
public void genericSetter(String key, Object value){
nameValueForListValue.put(key, value);
}
}
That whay any unknown field could be handle by this setter.
More info:#JsonAnySetter example
#JsonInclude(JsonInclude.Include.NON_NULL)
Is it possible to control the class name used when returning search results in Spring Data REST?
I have a class Account which is published as a JSON schema and does not include an id field, as that should be opaque in a RESTful API. In order to persist this using Spring Data MongoDB, I extend Account with PersistableAccount which has an id field.
When returning search results to the client, the name persistableAccounts is exposed, which is an implementation detail that should not leak into the API:
{
"_embedded" : {
"persistableAccounts" : [ {
"lastName" : "McLastName",
"firstName" : "Kevin",
"phoneNumber " : "+44 7700000000",
"email" : "kevin#example.com",
"_links" : {
"self" : {
"href" : "http://localhost:64712/accounts/id"
},
"persistableAccount" : {
"href" : "http://localhost:64712/accounts/id"
}
}
} ]
},
"_links" : {
"self" : {
"href" : "http://localhost:64712/accounts/search/findByFirstName?firstName=Kevin"
}
}
}
Is it possible to control the term used?
Spring Data Rest always includes a collectionRel in the reponse if your result may consist of multiple subtypes.
By default it is the class name, however, you can customize it within #RepositoryRestResource annotation on your repository.
For example:
#RepositoryRestResource(collectionResourceRel = "whateverIwantHere")
public interface CustomerRepository extends CrudRepository<Customer, Long> {
}
I am attempting to use Spring to Query a Mongo repository and filter an array subdocument. I have referenced how to filter array in subdocument with mongodb, but was wondering if there was a more appropriate or java structured method to do so using Spring.
I am currently using the shorthand repository interface notation, but I am getting back the complete document with the array not filtered.
PersonRepository.java
#Repository
public interface PersonRepository extends MongoRepository <Person, String> {
List<Person> findByAddressZipCode(#Param("zip") int zip);
}
Person.java
#Document
public class Person {
#Id
private String id;
private String firstName;
private String lastName;
private List<Address> address;
}
Address.java
public class Address {
private int zip;
}
Sample Input
{
"firstName":"George",
"lastName":"Washington",
"address":[{
"zip":"12345"
},{
"zip":"98765"
},{
"zip":"12345"
}]
}
Expected Output
{
"firstName":"George",
"lastName":"Washington",
"address":[{
"zip":"12345"
},{
"zip":"12345"
}]
}
Actual Output
{
"firstName":"George",
"lastName":"Washington",
"address":[{
"zip":"12345"
},{
"zip":"98765"
},{
"zip":"12345"
}]
}
Well, In Spring Data such kind of queries is not trivial.
Bad news:
Spring Data Repository does not have solution for MongoDB Aggregation. So, you cannot implement in MongoRepository any method to do so, like aggregateBy...
Good news:
Spring Data provides MongoTemplate class which allows you to execute complex queries, like you would do in standard MongoDB shell.
So, as you just want to exclude subdocument that does not match some condition, we need to define the aggregate pipelines.
I assume:
zip codes are Numeric (In your example is string)
And, to exclude subdocument, we filter by `zip`
There is no any other filter
MongoDB aggregation would be:
db.person.aggregate([
{$unwind: "$address"},
{$match: {"address.zip": 12345}},
{$group: { _id: { "firstName":"$firstName", "lastName":"$lastName", _id:"$_id" }, address: { $push: "$address" } } },
{$project: {_id:0, "firstName":"$_id.firstName", "lastName":"$_id.lastName", "address": "$address"}}
])
If all filters success, we got:
[
{
"address" : [
{
"zip" : 12345
},
{
"zip" : 12345
}
],
"firstName" : "George",
"lastName" : "Washington"
}
]
Now, in Spring Data way, you need add some changes in your project:
First, find your mongo-config.xml where you need to add:
<!-- Define the mongoDbFactory with your database Name -->
<mongo:db-factory uri="mongodb://user:pass#localhost:27017/db"/>
<!-- Define the MongoTemplate -->
<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
<constructor-arg name="mongoDbFactory" ref="mongoDbFactory" />
</bean>
MongoTemplate is the central class of the Spring’s MongoDB support providing feature sets to interact with the database. The template ... provides a mapping between your domain objects and MongoDB documents. More info
Second, in your #Service class, add following code to be loaded in #PostConstruct
#Autowired
private MongoOperations mongoOperations;
...
public List<Person> findByAddressZipCode(int zip) {
List<AggregationOperation> list = new ArrayList<AggregationOperation>();
list.add(Aggregation.unwind("address"));
list.add(Aggregation.match(Criteria.where("address.zip").is(zip)));
list.add(Aggregation.group("firstName", "lastName").push("address").as("address"));
list.add(Aggregation.project("firstName", "lastName", "address"));
TypedAggregation<Person> agg = Aggregation.newAggregation(Person.class, list);
return mongoOperations.aggregate(agg, Person.class, Person.class).getMappedResults();
}
Note: Both, Person and Address should have default empty constructor!
I want to create a JsonSchema file from my java classes using latest Jackson libraries .
When I have a java class like the following:
class MyClass {
String status;
}
how do I need to annotate the field so that the schema output would look like this:
{
...
"status": {
"description": "status",
"type": [ "string", "null" ],
"maxLength":12
}, ...
}
?
Cheers