How to accept JsonArray as input to WebAPI - asp.net-core

I am using .Net 6 WebAPI. I need to accept a generic JSON array as input. The properties present in each JSON document is not known before. So I cannot Deserialize to specific object type. So I am looking for 2 inputs.
a) What should be input datatype to accept this in body, when using System.Text.Json? Previously, we have used JArray using JSON.NET.
b) How can I then read this input as an array so that I can then convert into generic JsonObject type?
[
{ "prop1" : "value1", "prop2" : "value1"},
{ "prop3" : "value3", "prop4" : "value4"},
{ "propx" : "valuex", "propy" : "value6", "nested": { "other": [1,23,45] }}
]
I am also open to option of accepting NDJSON.
Thanks!

If all I do is create a new .net Web API from the standard template and add the following action:
[HttpPost]
public async Task<IActionResult> Post(JsonArray array)
{
foreach(var node in array)
{
Console.WriteLine(node);
}
return Ok();
}
And pass the content you have specified in the question using the built-in swagger UI:
And I can step through in the debugger and observe that the request has been deserialized to do what I please with:

From body you can receive ([FromBody]List<object> input) or ([FromBody]List<dynamic> input) and access to properties dynamically or serialize to typed objects.

Related

WSO2 Data Service query error : A JSON Array cannot be contained in the result records

I got this error when i was trying to invoke an sql query through a DataService .
The studenttest-1.0.0.dbs service, which is not valid, caused {1} DS Fault Message: A JSON Array cannot be contained in the result records
DS Code: UNKNOWN_ERROR
This is the Object to be returned from the DataService
<sql>SELECT * from etudiant where nom =?</sql>
<param name="nom" sqlType="STRING" />
<result outputType="json">
{
"Envelope": {
"Body": {
"GetFacturesClientResponse": {
"GetFacturesClientResult": {
"code": 0,
"nbreFactures": 2,
"totalResteAPayerFactures": 67.832,
"listeFactures": {
"Facture": [
{
"id":"$id",
"nom":"$nom",
"prenom":"$prenom",
"age":"$age",
"note":"$note"
}
]
},
"codeClient": "P-2008-043681",
"nom": "Oussama",
"prenom": "Haythem"
}
}
}
}
}
</result>
</query>```
You can't add complex JSON structures to the result mapping, you need to adhere to the following format. Read more here.
Also, the structure of the JSON template should follow some guidelines
in order to be compatible with the result. These guidelines are:
The top most item should be a JSON object. It cannot be a JSON array.
For handling multiple records from the result set, the immediate child
of the top most object can be a JSON array, and the array should
contain only a single object.
If only a single result is returned, the
immediate child of the top most object can be a single JSON object.
After the immediate child of the top most object, there cannot be
other JSON arrays in the mapping.
Hence try a simple mapping like below and add the rest of the JSON in the integration layer. (Interface the Dataservice with another API and modify the JSON structure within the API before responding to the client)
{
"listeFactures": {
"Facture": [
{
"id":"$id",
"nom":"$nom",
"prenom":"$prenom",
"age":"$age",
"note":"$note"
}
]
}
}

How to send a List<Map<String, String>> as parameter for a GET API

I have a requirement to pass List<Map<String, String>> as a parameter for REST GET API.
I need help to know how this can be passed from Postman or similar tool.
I tried to set it as a BODY for a GET API, it is giving me errors.
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.
Any help is appreciated.
You can very well !
I tried and this worked for me
Create a model class which has variable of type List<Map<String, String>> myList;
Define a controller similar to below
#PostMapping("/addList")
public ResponseEntity<List<Map<String, String>>> passList(#RequestBody ListModel listModel) {
System.out.println("List mapped " + listModel);
return new ResponseEntity<>(HttpStatus.CREATED);
}
Create a request from Postman or any tool like this
{
"myList": [
{
"one": "1",
"two": "2"
}
]
}
Response I got
List mapped ListModel [myList=[{one=1, two=2}]]
Make sure you map correct variable name ( for e.g. I have defined myList, so that must be passed so it gets properly mapped in Controller class ) also assuming toString, GetterSetters , and your familiarity with few basic REST annotations related to Spring/SpringBoot :)

C# removes duplicates in JSON request body

I've this endpoint:
[HttpPost]
[ODataRoute("some\odata\route")]
public async Task<HttpResponseMessage> Func_Name(Request_Type request)
{
...
}
request is IDictionary< string, string>. If user calls this endpoint with a JSON containing duplicates:
{
"Bob": "Doctor",
"Tim": "Engineer",
"Bob": "Sailor"
}
What I see in C# is:
{
"Tim": "Engineer",
"Bob": "Sailor"
}
Means, it always takes the last. How can I stop this automatic removal and see the duplicates after deserialization? Or making the endpoint fail on calls like these?
If you remove your parameter of your Post method you can try the following. Otherwise C# will try to automatically map the entity which will lead to an error.
var rawJson = await this.Request.Content.ReadAsStringAsync();
That will get you the raw JSON which will not be validated and hold the three properties: two of which have the "Bob" key. However I would consider this a bad idea and I'm puzzled by your use case.
C# cannot possibly convert your JSON to a Dictionary<string, string> seeing as the Key property of a Dictionary must always be unique. Needless to say that the JSON is also considered invalid when run through a validator.
{
"Bob": "Doctor",
"Tim": "Engineer",
"Bob": "Sailor"
}
I ended up switching to List instead of Dictionary

Remove HATEOS from spring data rest? [duplicate]

Using Spring Data REST with JPA in version 2.0.2.RELEASE.
How can I disable Hypertext Application Language (HAL) in the JSON ? http://stateless.co/hal_specification.html
I have tried many things already, but to no avail. For example, I have set Accept and Content-type headers to "application/json" instead of "application/hal+json" but I still receive the JSON content with hyper links.
For example, I'd like to get something like:
{
"name" : "Foo",
"street" : "street Bar",
"streetNumber" : 2,
"streetLetter" : "b",
"postCode" : "D-1253",
"town" : "Munchen",
"country" : "Germany",
"phone" : "+34 4410122000",
"vat" : "000000001",
"employees" : 225,
"sector" : {
"description" : "Marketing",
"average profit": 545656665,
"average employees": 75,
"average profit per employee": 4556
}
}
Instead of:
{
"name" : "Foo",
"street" : "street Bar",
"streetNumber" : 2,
"streetLetter" : "b",
"postCode" : "D-1253",
"town" : "Munchen",
"country" : "Germany",
"phone" : "+34 4410122000",
"vat" : "000000001",
"employees" : 225,
"_links" : {
"self" : {
"href" : "http://localhost:8080/app/companies/1"
},
"sector" : {
"href" : "http://localhost:8080/app/companies/1/sector"
}
}
}
Thanks for your help.
(Hyper)media types
The default settings for Spring Data REST use HAL as the default hypermedia representation format, so the server will return the following for the given Accept headers:
No header -> application/hal+json -> HAL
application/hal+json -> application/hal+json -> HAL
application/json -> application/json -> HAL (this is what the default configures)
application/x-spring-data-verbose+json -> application/x-spring-data-verbose+json -> a Spring Data specific format (using links for the links container and content as wrapper for the collection items.
If you configure RepositoryRestConfiguration.setDefaultMediaType(…) to a non-HAL format, the server will return the Spring Data specific JSON format unless you explicitly ask for application/hal+json. Admittedly the configuration option is probably a bit misleading, so I filed DATAREST-294 to improve this. The issue was resolved in 2.1 RC1 (Dijkstra) 2014.
Note that we effectively need a hypermedia format in place to be able to express relations between managed resources and enable discoverability of the server. So there's no way you'll be able to get rid of it completely. This is mostly due to the fact that you could easily crash the server if you expose entities that have bidirectional relationships or make up an enormous object graph.
Inlining related entities
If you never want to have sectors linked to and always inline them, one option is to simply exclude the SectorRepository from being exported as a REST resource in the first place. You can achieve this by annotating the repository interface with #RepositoryRestResource(exported = false).
To get a representation returned as you posted in your lower example have a look at the projections feature introduced in Spring Data REST 2.1 M1. It basically allow you to craft optional views on a resource that can differ from the default one via a simple interface.
You'd basically define an interface:
#Projection(name = "foo", types = YourDomainClass.class)
interface Inlined {
// list all other properties
Sector getSector();
}
If you either put this interface into a (sub)package of your domain class or manually register it via RepositoryRestConfiguration.projectionConfiguration() the resources exposing YourDomainClass will accept a request parameter projection so that passing in foo in this example would render the inlined representation as you want it.
This commit has more info on the feature in general, this commit has an example projection defined.
So you want 2 things:
1) get rid of _links field
2) include the related sector field
Possible solution (works for me :D)
1) get rid of _links
For this create the class below:
[... package declaration, imports ...]
public class MyRepositoryRestMvcConfiguration extends RepositoryRestMvcConfiguration {
public MyRepositoryRestMvcConfiguration(ApplicationContext context, ObjectFactory<ConversionService> conversionService) {
super(context, conversionService);
}
#Bean
protected LinkCollector linkCollector() {
return new LinkCollector(persistentEntities(), selfLinkProvider(), associationLinks()) {
public Links getLinksFor(Object object, List<Link> existingLinks) {
return new Links();
}
};
}
}
and use it e.g.:
[... package declaration, imports ...]
#SpringBootApplication
#Import({MyRepositoryRestMvcConfiguration.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I'm pretty sure (99%, but not tested) that you won't need this class for removing the _links for the related entity/entities included the way next point (2) is showing.
2) include the related sector field
For this you could use Excerpts (especially made for this scenario). Because the Spring example is so eloquent and it's silly to just copy it here I'll just point it: https://docs.spring.io/spring-data/rest/docs/3.1.x/reference/html/#projections-excerpts.excerpting-commonly-accessed-data.
But just for the record and your convenience I'll paste the main parts of the spring example:
#Projection(name = "inlineAddress", types = { Person.class })
interface InlineAddress {
String getFirstName();
String getLastName();
Address getAddress();
}
see at Projection javadoc that types means The type the projection type is bound to.
The excerpt could be used this way:
#RepositoryRestResource(excerptProjection = InlineAddress.class)
interface PersonRepository extends CrudRepository<Person, Long> {}
in order to get this (when also using MyRepositoryRestMvcConfiguration):
{
"firstName" : "Frodo",
"lastName" : "Baggins",
"address" : {
"street": "Bag End",
"state": "The Shire",
"country": "Middle Earth"
}
}
For you the sector is the equivalent of address.
Final notes
When returning arrays the _links field won't be removed (it's too intrusive to do it); in the end you'll have something like this:
{
"_embedded" : {
"persons" : [ {person1}, {person2}, ..., {personN} ]
},
"_links" : {
e.g. first, next, last, self, profile
},
"page" : {
"size" : 1,
"totalElements" : 10,
"totalPages" : 10,
"number" : 0
}
}
As you can see even if we'd have _links removed that still won't be enough; one would probably also want _embedded replaced by persons which would lead to less maintainable code (too much spring intrusive overrides). But if one really wants these too he should start checking RepositoryRestMvcConfiguration and RepositoryEntityController.getCollectionResource.
Spring is evolving so I feel the need to point that this works with at least:
spring-data-rest-webmvc 3.1.3.RELEASE
or, if you prefeer spring boot version:
spring-boot-starter-parent 2.1.1.RELEASE
If you want to delete _links do the following (worked for me):
Go to your pom.xml and delete the following dependency:
spring-boot-starter-data-rest
Make an "Update project" to update the pom.xml changes.
Now it will be use your own controller for the api rest, deleting _self, _links..., that alike this:
[... package declaration, imports ...]
#RestController
#RequestMapping("/series")
public class SerieController {
#Autowired
private SerieRepositorio serieRepositorio;
public SerieController(SerieRepositorio serieRepositorio) {
this.serieRepositorio = serieRepositorio;
}
#GetMapping
public Iterable<Serie> getAllSeries() {
return serieRepositorio.findAll();
}
}

Passing a JSON object to worklight java adapter

I would like to pass a complete JSON object to a java adapter in worklight. This adapter will call multiple other remote resources to fulfill the request. I would like to pass the json structure instead of listing out all of the parameters for a number of reasons. Invoking the worklight procedure works well. I pass the following as the parameter:
{ "parm1": 1, "parm2" : "hello" }
Which the tool is fine with. When it calls my java code, I see a object type of JSObjectConverter$1 being passed. In java debug, I can see the values in the object, but I do not see any documentation on how to do this. If memory serves me, the $1 says that it is an anonymous inner class that is being passed. Is there a better way to pass a json object/structure in adapters?
Lets assume you have this in adapter code
function test(){
var jsonObject = { "param1": 1, "param2" : "hello" };
var param2value = com.mycode.MyClass.parseJsonObject(jsonObject);
return {
result: param2value
};
}
Doesn't really matter where you're getting jsonObject from, it may come as a param from client. Worklight uses Rhino JS engine, therefore com.mycode.MyClass.parseJsonObject() function will get jsonObject as a org.mozilla.javascript.NativeObject. You can easily get obj properties like this
package com.mycode;
import org.mozilla.javascript.NativeObject;
public class MyClass {
public static String parseJsonObject(NativeObject obj){
String param2 = (String) NativeObject.getProperty(obj, "param2");
return param2;
}
}
To better explain what I'm doing here, I wanted to be able to pass a javascript object into an adapter and have it return an updated javascript object. Looks like there are two ways. The first it what I answered above a few days ago with serializing and unserializing the javascript object. The other is using the ScriptableObject class. What I wanted in the end was to use the adapter framework as described to pass in the javascript object. In doing so, this is what the Adapter JS-impl code looks like:
function add2(a) {
return {
result: com.ibm.us.roberso.Calculator.add2(a)
};
The javascript code in the client application calling the above adapter. Note that I have a function to test passing the javascript object as a parameter to the adapter framework. See the invocationData.parameters below:
function runAdapterCode2() {
// x+y=z
var jsonObject = { "x": 1, "y" : 2, "z" : "?" };
var invocationData = {
adapter : "CalculatorAdapter",
procedure : 'add2',
parameters : [jsonObject]
};
var options = {
onSuccess : success2,
onFailure : failure,
invocationContext : { 'action' : 'add2 test' }
};
WL.Client.invokeProcedure(invocationData, options);
}
In runAdapterCode2(), the javascript object is passed as you would pass any parameter into the adapter. When worklight tries to execute the java method it will look for a method signature of either taking an Object or ScriptableObject (not a NativeObject). I used the java reflection api to verify the class and hierarchy being passed in. Using the static methods on ScriptableObject you can query and modify the value in the object. At the end of the method, you can have it return a Scriptable object. Doing this will give you a javascript object back in the invocationResults.result field. Below is the java code supporting this. Please note that a good chunk of the code is there as part of the investigation on what object type is really being passed. At the bottom of the method are the few lines really needed to work with the javascript object.
#SuppressWarnings({ "unused", "rawtypes" })
public static ScriptableObject add2(ScriptableObject obj) {
// code to determine object class being passed in and its heirarchy
String result = "";
Class objClass = obj.getClass();
result = "objClass = " + objClass.getName() + "\r\n";
result += "implements=";
Class[] interfaces = objClass.getInterfaces();
for (Class classInterface : interfaces) {
result += " " + classInterface.getName() ;
}
result += "\r\nsuperclasses=";
Class superClass = objClass.getSuperclass();
while(superClass != null) {
result += " " + superClass.getName();
superClass = superClass.getSuperclass();
}
// actual code working with the javascript object
String a = (String) ScriptableObject.getProperty((ScriptableObject)obj, "z");
ScriptableObject.putProperty((ScriptableObject)obj, "z", new Long(3));
return obj;
}
Note that for javascript object, a numeric value is a Long and not int. Strings are still Strings.
Summary
There are two ways to pass in a javascript object that I've found so far.
Convert to a string in javascript, pass string to java, and have it reconstitute into a JSONObject.
Pass the javascript object and use the ScriptableObject classes to manipulate on the java side.