Understanding Spring HATEOAS error: Embedded wrapper returned null for both the static rel and the rel target type - spring-hateoas

As mentioned, I would like to get help understanding this error which is quite cryptic to me:
WARN 6436 --- [nio-8080-exec-4] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: Embedded wrapper org.springframework.hateoas.server.core.EmbeddedWrappers$EmbeddedCollection#7538c537 returned null for both the static rel and the rel target type! Make sure one of the two returns a non-null value!; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Embedded wrapper org.springframework.hateoas.server.core.EmbeddedWrappers$EmbeddedCollection#7538c537 returned null for both the static rel and the rel target type! Make sure one of the two returns a non-null value! (through reference chain: org.springframework.hateoas.CollectionModel["_embedded"])]
This error came about after I tried accessing a collection model through the following method within my Spring Controller. What I'm trying to do is to return all the share transactions associated with a particular stock code.
#Autowired
private ShareTransactionAssembler shareTransactionAssembler;
#GetMapping("/{stockCode}/transactions")
public CollectionModel<ShareTransactionModel> getShareTransactions (#PathVariable("stockCode") String stockCodeString) {
StockCode stockCode = new StockCode(stockCodeString);
List<ShareTransaction> shareTransactionList = stockQueryService.getShareTransactions(stockCode);
return shareTransactionAssembler.toCollectionModel(shareTransactionList, stockCode);
}
The following is my shareTransactionAssembler code:
#Component
public class ShareTransactionAssembler extends RepresentationModelAssemblerSupport<ShareTransaction, ShareTransactionModel> {
public ShareTransactionAssembler() {
super(StockController.class, ShareTransactionModel.class);
}
#Override
public ShareTransactionModel toModel(ShareTransaction shareTransaction) {
String stockCodeString = shareTransaction.getStock().getStockCode().getStockCodeAsString();
Link selfLink = linkTo(
methodOn(StockController.class)
.findShareTransaction(stockCodeString, shareTransactionModel.getId())
)
.withSelfRel();
shareTransactionModel.add(selfLink);
Link stockLink = linkTo(
methodOn(StockController.class)
.findStock(stockCodeString)
)
.withRel("Stock");
shareTransactionModel.add(stockLink);
return shareTransactionModel;
}
public CollectionModel<ShareTransactionModel> toCollectionModel (List<ShareTransaction> shareTransactionList, StockCode stockCode) {
CollectionModel<ShareTransactionModel> shareTransactionModels = super.toCollectionModel(shareTransactionList);
Link selfLink = linkTo(
methodOn(StockController.class)
.getShareTransactions(stockCode.getStockCodeAsString())
)
.withSelfRel();
shareTransactionModels.add(selfLink);
return shareTransactionModels;
}
}
In case you need to know, accessing the individual share transactions seem to work fine. An example JSON response is as follows:
{
"id": 1,
"transactPrice": "13.00",
"commissionPaid": "1.05",
"transactTimeDate": "2020-03-07T13:05",
"transactUnits": 30,
"_links": {
"self": {
"href": "http://localhost:8080/stock/NVDA/transactions/1"
},
"Stock": {
"href": "http://localhost:8080/stock/NVDA"
}
}
}
I would really appreciate if you can shed some light on this! Thanks!

I stumbled onto this error as well. I got it fixed by extending my model class with RepresentationModel instead of EntityModel.
Still searching why this makes a difference..

I had this problem too and this is my understanding based on reading the documentation here (see section 2.4):
https://docs.spring.io/spring-hateoas/docs/current/reference/html/#reference
You are not supposed to directly inherit from EntityModel. EntityModel is a generic RepresentationModel implementation that can be used for representing singular objects or concepts. You can use it when you don't want to write your own RepresentationModel. It should just serialize all fields in the object you pass it.
RepresentationModel is the root class for defining your own representation models.

Related

JSON to #RelationshipEntity (via Spring Boot)

I'm learning Neo4j and using it through spring-data-neo4j, but I'm struggling to make it work for my needs.
Basically I want to represent this:
A -[{...relationshipKey}]-> B
i.e. A contains a map of Bs, and each key of the map is represented by the property relationshipKey. I do this because AFAIU it's not possible for Neo4J to store a map.
So I'm using #RelationshipEntity like this:
#Data
#NodeEntity
class A {
#Relationship(type = "CONTAINS_B")
Set<ContainsB> containsB;
}
#RelationshipEntity(type = "CONTAINS_B")
#Data
class ContainsB {
#GraphId Long id;
#StartNode A a;
#EndNode B b;
#Property RelationshipKey relationshipKey; // an enum
}
#Data
#NodeEntity
class B {
// Whatever
}
And I'm trying to save a new graph using a spring-boot controller to which I pass a JSON.
Controller:
#PostMapping(value = BASE_PATH + "/create")
A createA(#RequestBody #NonNull A a) {
return ARepository.save(a);
}
JSON:
{
"containsB": [{
"relationshipKey": "key1",
"b": {"someB": "properties"}
},{
"relationshipKey": "key2",
"b": {"someB": "different properties"}
}]
}
Yet when I invoke the controller, I get this exception:
org.neo4j.ogm.exception.MappingException: Relationship entity A(...)
cannot have a missing start or end node
When I debug the controller invocation, a.getContainsB().iterator().next().getA() is null but a.getContainsB().iterator().next().getB() is instantiated and has the correct properties.
If I do this before the repository saving, then it seems to works fine, and A and B are correctly related:
a.getContainsB().iterator().next().setA(new A())
Is there a way to pass a JSON that will be correctly interpreted by Neo4J?
Reason for the error
Your JSON is defining an instance of A with two relationships "ContainsB", which are represented by a #RelationshipEntity in the domain model. However, the definition of "ContainsB" in the JSON does not itself contain a reference to its enclosing A, so when each ContainsB is created it has no reference A.
Solutions
There are a couple of things you could try.
The obvious one is to create instances of "ContainsB" instead:
{
"relationshipKey": "key1",
"a": { ... },
"b": { ... }
}
Another option is to use a JSON generator that allows you to create object references and refer to them internally in your definition. The JSOG format will allow you to do this, and is compatible with Jackson. See https://github.com/jsog/jsog for more details.

JSON, JAXB,JSON Rest

I created a Rest Service which has is working fine in XML request ( Rest Service input JAXB generated class ). When I am trying to send a Json Request, It's throwing an exception.
SEVERE: The exception contained within MappableContainerException could not be mapped to a response, re-throwing to the HTTP container
org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class Transaction] from JSON String; no single-String constructor/factory method (through reference chain:
class Transaction which is inner class .
My Request class is generated from schema using JAXB, My getter method is like below but no setter method.
public List<Transaction> getORIG() {
if (origtrans == null) {
origtrans = new ArrayList<Transaction>();
}
return this.origtrans;
}
My Json Request below
{
"LOB_CD": "42424"
"ORIGINALTRANSACTION" : [
"LOGON_ID" : "TEST"
]
}
When I am addiong original Transaction it's throwing an error otherwise it's working fine.
Can you please help me out with this.
Thanks in advance.
First of all make sure you are using a valid JSON, like:
{
"LOB_CD": "42424",
"ORIGINALTRANSACTION": {
"LOGON_ID": "TEST"
}
}
Also, it would be nice if you could show us the Transaction class.
It works.
I did following steps
1) Added an empty constructor
2) Added this annotation for the list
#JsonDeserialize(as=ArrayList.class, contentAs=Transaction.class)
3) change a Json Object to
{ "LOB_CD": "42424" "ORIGINALTRANSACTION" : [ { "LOGON_ID" : "TEST" } ] }
Thanks a lot,
Patel

Why is my Web API controller action not deserializing child properties of child properties?

I'm sending a json payload in a PUT request to a web API controller action. The action in question has a signature that looks like this:
public IHttpActionResult Post([FromBody]SaveThingRequest request)
SaveThingRequest looks something like this:
public class SaveThingRequest
{
public List<ElementInfo> Elements { get; set; }
public class ElementInfo
{
public List<ElementSettingInfo> Settings { get; set; }
public class ElementSettingInfo
{
public string Name { get; set; }
public string Value { get; set; }
}
}
}
I'm posting json in the body of the request that contains Elements that have Settings. I've confirmed this by manually deserializing in the controller action and confirming that the JSON has a structure that looks something like:
{
Elements: [
{
Settings: [
{
Name: 'Name 1',
Value: 'Value 1'
},
{
Name: 'Name 2',
Value: 'Value 2'
}
]
},
{
Settings: [
{
Name: 'Name 1',
Value: 'Value 1'
},
{
Name: 'Name 2',
Value: 'Value 2'
}
]
}
]
}
However, when .NET deserializes the payload and creates the SaveThingRequest, my Elements are populated but all of them have a null Settings property. I don't know how else to troubleshoot this. Does anyone have any thoughts on what might be going on here?
This question should be deleted. It works as advertised. My problem was that I had an additional property on the JSON called 'settings' (lower-case) that the deserializer was trying to match because NewtonSoft deserialization attempts a non-case sensitive match if a case sensitive one isn't found. I was able to discover what was happening by changing the signiture of the method to:
public IHttpActionResult Post([FromBody]string request)
and then adding this to the method implementation:
var result = JsonConvert.DeserializeObject<SaveThingRequest>(request);
I got a deserialization exception saying:
Cannot deserialize the current JSON object (e.g. {"name":"value"})
into type
'System.Collections.Generic.List`1[Noteable.Contracts.ClinicalReports.SaveThingRequest+ElementInfo+ElementSettingInfo]'
because the type requires a JSON array (e.g. [1,2,3]) to deserialize
correctly. To fix this error either change the JSON to a JSON array
(e.g. [1,2,3]) or change the deserialized type so that it is a normal
.NET type (e.g. not a primitive type like integer, not a collection
type like an array or List) that can be deserialized from a JSON
object. JsonObjectAttribute can also be added to the type to force it
to deserialize from a JSON object. Path
'Elements[0].settings.TextSize', line 11, position 20.
The end of the message showed me what was being deserialized when the deserializer failed and pointed me in the right direction. =[
I was stuck in the same problem. However, the problem was something else.
In my case, the property that was not getting serialize was declared without public access specifier. I declared it to public, and it ran perfectly fine.
Hope this will help someone who got stuck in this situation. It took me 20 mins to reach this solution.
Make sure you didn't do something stupid like I did and sent a partially 'stringified' JSON string from the browser instead of a JSON object.
See the problem? I didn't at first until I tried deserializing it from a string and then I realized that shippingAddress itself was a string instead of being an actual JSON object. The rest of the object was proper JSON, but I accidentally serialized the shippingAddress field.
{
"fspID": 571285,
"foo": 444,
"shippingAddress": "{\"countryCode\":\"US\",\"firstName\":\"Test User\",\"lastName\":null,\"address1\":\"1 Main St4\",\"address2\":null,\"company\":null,\"city\":\"San Jose\",\"stateOrProvince\":\"California\",\"stateCd\":\"CA\",\"zipOrPostal\":\"95131\",\"countryName\":\"United States\",\"countryDesc\":\"United States\",\"phone\":null,\"phone2\":null}"
}
If you are using JSON.NET and the attribute [JsonProperty] to name a property – make sure you haven't accidentally copy pasted the same property twice and forgotten to update the string value.
[JsonProperty("billingAddress")]
public Address BillingAddress { get;set; }
[JsonProperty("billingAddress")]
public Address ShippingAddress { get;set; }
This will throw a Newtonsoft.Json.JsonSerializationException that won't actually be visible to you and will screw up the model.
I had the same problem that the child property was null. However the problem was something else.
I had only a getter on the property, once I added a setter as well, then the property was set as expected.

Add 'standard' JSON fields to serialized objects in REST API

Motivated by this: Google JSON Style Guide, I want to insert a bit of custom serialization logic to my rest API. I'm using the WebAPI 2 and JSON.NET. My goal is to wrap the 'payload' of my response in the 'data' field of the main JSON response, as described in the style guide, include an apiVersion field in every response, and that sort of thing. Of course the controller actions just return straight POCO's, and I want to modify the container that they're sent inside of, not the POCOs themselves, so:
{
"id": "111",
"apiVersion": "1.0",
"data": {
"kind": "monkey",
"name": "manny",
"age": "3"
},
"error": null
}
...that type of thing. So I envision inserting little bits of standard data into every response before it goes over the wire. What's the best way to accomplish this?
TIA.
I believe you can use an ActionFilterAttribute to achieve this kind of behaviour. You would first need to create a class to represent your wrapped response (all the properties are string, adjust as you need):
public class WrappedJsonResponse
{
public string Id {get;set;}
public string ApiVersion {get;set;}
public object Data {get;set;}
public string Error {get;set;}
}
The ActionFilterAttribute allow you to do some processing after the execution of an action via the virtual OnActionExecuted method:
public class WrappedJsonAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(HttpActionExecutedContext context)
{
// A POCO response will normally be wrapped in an ObjectContent
var content = context.Response.Content as ObjectContent
if(content != null)
{
// Create the WrappedJsonResponse object appropriately and
// put the original result in the Data property
content.Value = new WrappedJsonResponse { Data = content.Value };
content.ObjectType = typeof(WrappedJsonResponse);
}
}
}
With the attribute, you can then choose to apply it where you want (whole controller, action only or as a default filter).
Note: I do not have access to a development environment at the moment and have not tested the filter. If this is not complete, it should at least give you an idea on how it can be done.

Jackson JSON to Java mapping for same attrubute with different data type

I have a JSON object which I don't have control of and want to map it to a Java object which is pre-created.
There is one attribute in the JSON object which can be a URL or it could be a JSONArray.
Class SomeClass {
private URL items;
public URL getURL() {
return items;
}
public void setURL(URL url) {
this.items = url;
}
}
Below is the JSON:
Case A:
{
...
items: http://someurl.abc.com/linktoitems,
...
}
OR
Case B
{
...
items: [
{ "id": id1, "name": name1 },
{ "id": id2, "name": name2 }
]
...
}
If i create the POJO to map for Case A, Case B fails and vice versa. In short, is there a way to map the JSON attribute to the POJO field with different data types? In that case I will create two separate fields in the POJO named,
private URL itemLink;
private Item[] itemList;
It depends on exact details, but if what you are asking is if it is possible to map either JSON String or JSON array into a Java property, yes this can be done.
Obvious way would be to define a custom deserializer which handles both kinds of JSON input.
But it is also possible to define Java type in such a way that it can be constructed both by setting properties (which works from JSON Object) and have a single-String-arg constructor or static single-String-arg factory method marked with #JsonCreator.
Yet another possibility is to use an intermediate type that can deserialized from any JSON: both java.lang.Object and JsonNode ("JSON tree") instances can be created from any JSON. From this value you would need to do manual conversion; most likely in setter, like so:
public void setItems(JsonNode treeRoot) { .... }
What will not work, however, is defining two properties with the same name.
One thing I don't quite follow is how you would convert from List to URL though. So maybe you actually do need two separate internal fields; and setter would just assign to one of those (and getter would return value of just one).