I have a model with a List<string> Parameters {get; set;} property, so when I send from postman a json like this
{
...,
"Parameters" :
[
["FirstParam"],
["SecondParam"]
],
...
}
I receive null in my controller method, I mean the requestModel is null
public JsonResult GetBalances([FromBody]RequestModel requestModel)
{
...
}
I know that "Parameters" is the problem because when I send without it, I receive the model normally.
How can I receive a list of parameters(string) from a json format?
PD. It works when I create a model like
public class RequestModel
{
...
public List<Parameters> Parameters {get;set;}
}
public class Parameters
{
public string Name {get;set;}
}
And the json
{
...,
"Parameters" : [
{"Name": "FirstParameter"},
{"Name":"SecondParameter"}
]
...
}
But I wonder if it is possible get it without create a class. Thanks in advance.
I found the problem, the json format was wrong. My final json:
{
...,
"Parameters" :
[
"FirstParam",
"SecondParam"
],
...
}
I think I was sending a list of arrays of strings (or something like that). Now I can use List<string> as a property in my model.
Related
I'm kind of a fresher when it comes to doing API work especially with JSON.
Here's what my code looks like...
Endpoing:
[HttpPost("postWithBody")]
public async Task<IActionResult> PostWithBody (string param1, [FromBody] object requestBody)
{
var x = JsonSerializer.Deserialize<ParamModel>(requestBody); <-- Error cannot convert from 'object' to System.ReadOnlySpan<byte>
return ok(param1); <--this here just so it doesn't bark at me
}
SO in the above code, I'm erroring out on (RequestBody) with this error:
Error cannot convert from 'object' to System.ReadOnlySpan
public class ParamModel
{
public string PName {get;set;}
public string PValue {get;set;}
}
But essentially to finish the demo of what I'm trying to accomplish is, goal is to pass JSON value to this endpoint in the body that looks like this:
{
"Param1": "XXX",
"Param2": "111"
}
and my goal would be for CustomParams model class to have the
PName = Param1
PValue = "XXX"
and
PName = "Param2"
PValue = "111"
Is this the correct approach I'm taking?
Thank you.
EDIT: I guess I could do something like: [FromBody] ParamModel requestBody
and I did try it, when I pass JSON like this, it came as null in the endpoint:
{"test":"hey"}
But also, I probably would need to do something like this, since I want to have the option of passing multiple params.
public class ParamList
{
public List<ParamModel> data {get;set;}
}
and have that be [FromBody] ParamList requestBody
First of all I would suggest that you use the model in the action parameter and let the framework do the deserialisation for you:
public async Task<IActionResult> PostWithBody(
string param1, [FromBody] ParamModel requestBody)
Now you will be able to post JSON in that matches that object, something like this for example:
{
"PName": "test",
"PValue": "hey"
}
In your update, you say that you would like instead to use the ParamList object. In that case, you would need JSON that matches, something like this:
{
"data": [
{ "PName": "test1", "PValue": "hey1" },
{ "PName": "test2", "PValue": "hey2" }
]
}
Now in your action you can loop over the list like this:
foreach(var param in requestBody.data)
{
var paramName = param.PName;
var paramValue = param.PValue;
// etc.
}
I'm trying to set up a series of complex app settings in a separate settings.json file - I won't go into detail as to why...
So I have a JSON file which looks like this:
{
"Website": {
"Name": "Website Name",
"api_key": "----------",
"domain": "-----------"
},
"Pages": {
"Index": {
"Name": "Index",
"Widgets": {
"BestSellers": {
"Name": "BestSellers",
"Type": "ProductCollection",
"Data": {
"Limit": "8",
"Sort": {
"SortType": 3
},
"GetFullProducts": true,
"GroupVariations": false
}
}
}
}
}
}
The first section "Website" simply fetches string settings, all working fine.
The section section "Pages" is more complicated. I have classes that look like this:
public class PageSettings
{
public string Name { get; set; }
public Dictionary<String, Widget> Widgets { get; set; }
public class Widget
{
public string Name { get; set; }
public string Type { get; set; }
public Dictionary<String, object> Data { get; set; } // THIS IS THE PROPERTY THIS QUESTION IS ABOUT
}
}
I use this code to deserialise the above:
IConfigurationSection pagessection = root.GetSection("Pages");
if (pagessection.Exists())
{
_Pages = new Dictionary<String, PageSettings>();
pagessection.Bind(_Pages);
}
With the JSON File exactly as above, this will fail. For some reason, the nested Object Sort in the Data property cannot be deserialised as Object:
"Sort": {
"SortType": 3
}
If I take the above nested object out then all the code so far will work. However, there are use cases where I need that nested object.
I have tried using ExpandoObject which is very cool and clever, but because it expects KeyValuePairs, it then only serialises the nested object in Data, ignoring the simple properties Limit, GetFullroduct etc.
So what I need is a form of ExpandoObject which can also be ExpandoString or something?!
Alternatively... I need to be able to get the Data property from the settings.json file in String form and explicitly deserialise it using JsonConvert.Deserialize at the point of use, because at that point I can declare the proper class that it needs to be deserialised to, but i can't seem to find a way to get the IConfigurationSection code to get the value as a string, rather than a JSON object.
I can't find a solution to this, the nested object breaks everything I have tried.
The helpful comment from #Fei Han has helped a little in highlighting the flexibility of the JObject class, so the solution I have come to is this:
The complex class has to be stored as an HTML encoded string in the settings.json file:
"Data": "{"Limit": "8","GetFullProducts":true,"GroupVariations":true, "Sort": {"SortType": 3}}"
it has to be HTMLEncoded because it is the only way I can find to make the ConfigurationBuilder treat it as a string so that I can cast it correctly later.
The corresponding Class for this now has these properties:
public string ModelString { get; set; }
public Newtonsoft.Json.Linq.JObject Model
{
get
{
string s = ModelString.HtmlDecode();
if (s.EmptyStr())
{
return new JObject();
} else {
return JObject.Parse(s);
}
}
}
From this I am able to easily cast my Data to the eventually required class using .ToObject<MyObject>()
For some reason, this works. I am able to deserialise the string to a JObject in this method, but not directly using the Bind command on IConfigurationSection.
If anyone has any tips on why Bind won't do it, that'd be interesting!
I have the following POJO. It contains data about my airport.
class MyClass
{
#JsonProperty("AirportCode")
String airportCode;
#JsonProperty("AirportID")
Integer airportId;
}
The POJO objects are created using a JSON received from an API. the API sample output is like below.
[
{
"AirportCode": "BBA",
"AirportID": 4276802,
},
{
"AirportCode": "SCQ",
"AirportID": 5325651,
}
]
My code is to function as follows
void func()
{
//Get JSON from API and convert to POJO
//Do some processing on the POJO
//Convert POJO into JSON and write to file
}
The file contents are as follows
[
{
"AirportCode": "BBA-IN",
"AirportID": 4276802,
},
{
"AirportCode": "SCQ-USA",
"AirportID": 5325651,
}
]
I however require the output to be in camel case (like the POJO)
[
{
"airportCode": "BBA-IN",
"airportId": 4276802,
},
{
"airportCode": "SCQ-USA",
"airportId": 5325651,
}
]
Is there anyway I can get #JsonProperty to be honored only during deserialisation to POJO and not during serialization to JSON?
Have you tried to annotate getters and setters with different jsonProperty values? Directly it won't work but if you change the getter/setter name it should as both methods will be processed as if they belonged to different fields.
Something like this:
class MyClass {
String airportCode;
Integer airportId;
#JsonProperty("airportCode")
public String getAirportCodeForJson() {
return airportCode;
}
#JsonProperty("AirportCode")
public void setAirportCode(String airportCode) {
this.airportCode = airportCode;
}
#JsonProperty("airportID")
public Integer getAirportIdForJson() {
return airportId;
}
#JsonProperty("AirportID")
public void setAirportId(Integer airportId) {
this.airportId = airportId;
}
}
You could deserialize into a Dictionary and then camel-case all keys. Afterwards you'd be able to use it as intended.
I am looking for a way to include only certain fields from the JSON request received to my service and then log them accordingly to avoid logging too much data.
The fields to include and hence log would be configured in a properties file.
Since I use Spring Boot for my service, Jackson JARs should already be available.
Since the request JSON is complex with nested arrays and objects as fields am not sure if I can achieve my requirement with Jackson.
Is there a way to extract only certain fields along with their values from a input request JSON using the Jackson API?
Basically, I am looking at 2 use cases.
1.Select one or more elements (which I intend to pass by config) from Json String and then render them
2.Select and update one or more elements inline. I want to mask the values of the elements before rendering them.
I am providing the code for selecting the element and Json which I used along with what I expect as below.
public String getValues(String key,String jsonString){
String fieldNodeStr =null;
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
JsonNode fieldNode = node.at(key);
fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
System.out.println(fieldNodeStr);
} catch (IOException e) {
System.out.println("IOException:",e);
}catch (Exception e) {
System.out.println("Exception:",e);
}
}
My Json is as below,
{
"employeeId" : "5353",
"salesId" : "sales005",
"manager" : {
"userId" : "managerUser",
"isSuperUser" : false,
"firstName":"manager first name",
"lastName":"manager last name"
},
"administrator" : {
"userId" : "administratorUser",
"isSuperUser" : false,
"firstName":"admin first name",
"lastName":"admin last name"
},
"requester" : [
{
"id":"1234",
"demographic" : {
"firstName" : "hello",
"lastName" : "hai"
}
},
{
"id":"1235",
"demographic" : {
"firstName" : "welcome",
"lastName" : "user"
}
}
]
}
I have 2 issues as below.
If I pass "/manager/userId" ,"/administrator/isSuperUser" (OR) "/salesId" I am able to get the expected value.
But, If want to get the /requester/id (OR) /requester/demographic (OR) /requester/demographic/lastName , I am not able to fetch.
I am getting null values.
I expect the following when I pass , "/requester/id" (OR) "/requester/demographic" respectively.
"requester" : [
{
"id":"1234"
},
{
"id":"1235"
}
]
"requester" : [
{
"demographic" : {
"firstName" : "hello",
"lastName" : "hai"
}
},
{
"demographic" : {
"firstName" : "welcome",
"lastName" : "user"
}
}
]
Along with fetch I also want to update the values inline after locating them with JsonPointer
I have my code as below for the updation,
public String findAndUpdate(String key,String jsonString,String repValue){
String fieldNodeStr =null;
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(jsonString);
JsonNode fieldNode = node.at(key);
//Update the value of located node to a different one
((ObjectNode) fieldNode).put(key,repValue);
fieldNodeStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(fieldNode);
System.out.println(fieldNodeStr);
} catch (IOException e) {
System.out.println("IOException:",e);
}catch (Exception e) {
System.out.println("Exception:",e);
}
return fieldNodeStr;
}
When I pass, "/manager/userId" as value of key, I get the below error,
17:21:24.829 [main] ERROR com.demo.jsondemo.TestClass - Exception:
java.lang.ClassCastException: com.fasterxml.jackson.databind.node.TextNode cannot be cast to com.fasterxml.jackson.databind.node.ObjectNode
at com.demo.jsondemo.TestClass.findAndUpdate(TestClass.java:95) [classes/:na]
at com.demo.jsondemo.TestClass.main(TestClass.java:221) [classes/:na]
JSON Pointer
You could use JSON Pointer (a string syntax for identifying a specific value within a JSON document) defined by the RFC 6901.
For example purposes, consider the following JSON:
{
"firstName": "John",
"lastName": "Doe",
"address": {
"street": "21 2nd Street",
"city": "New York",
"postalCode": "10021-3100",
"coordinates": {
"latitude": 40.7250387,
"longitude": -73.9932568
}
}
}
To query the coordinates node, you could use the following JSON Pointer expression:
/address/coordinates
JSON Pointer and Jackson
Jackson 2.3.0 introduced support to JSON Pointer and it can be used as following:
String json = "{\"firstName\":\"John\",\"lastName\":\"Doe\",\"address\":{\"street\":"
+ "\"21 2nd Street\",\"city\":\"New York\",\"postalCode\":\"10021-3100\","
+ "\"coordinates\":{\"latitude\":40.7250387,\"longitude\":-73.9932568}}}";
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
JsonNode coordinatesNode = node.at("/address/coordinates");
The coordinates node could be parsed into a bean:
Coordinates coordinates = mapper.treeToValue(coordinatesNode, Coordinates.class);
Or can be written as String:
String coordinatesNodeAsString = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(coordinatesNode);
Jayway JsonPath
A good alternative to JSON Pointer is JSONPath. In Java, there's an implementation called Jayway JsonPath, which integrates with Jackson.
To query the coordinates node with JsonPath, you would use the following expression:
$.address.coordinates
And the code to query the node would be like:
JsonNode coordinatesNode = JsonPath.parse(json)
.read("$.address.coordinates", JsonNode.class);
JsonPath requires the following dependency:
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.2.0</version>
</dependency>
And, to integrate with Jackson, following lines are required:
Configuration.setDefaults(new Configuration.Defaults() {
private final JsonProvider jsonProvider = new JacksonJsonProvider();
private final MappingProvider mappingProvider = new JacksonMappingProvider();
#Override
public JsonProvider jsonProvider() {
return jsonProvider;
}
#Override
public MappingProvider mappingProvider() {
return mappingProvider;
}
#Override
public Set<Option> options() {
return EnumSet.noneOf(Option.class);
}
});
Update based on your requirements
Based on the additional details you have provided in your question, I would say JSONPath will offer you more flexibility the JSON Pointer.
You can try the following:
Instead of /requester/id, use $.requester[*].id.
Instead of /requester/demographic, use $.requester[*].demographic.
These expressions can be tested online. Have a look at the following resources:
JSONPath Online Evaluator
JSONPath Expression Tester
And read the Jayway JsonPath documentation to understand how to use it properly.
Jackson's JsonView annotations should allow you to mark certain fields/getters with a particular view. Something like the following should work.
public class Item {
public static class LoggingView {}
#JsonView(LoggingView.class)
public int id;
#JsonView(LoggingView.class)
public String name;
public byte[] data;
}
This should allow you to write id and name without writing data by doing the following:
public class Something {
private static final Logger logger = LoggerFactory.getLogger();
private static final ObjectWriter loggingItemWriter = new ObjectMapper().writerWithView(Item.LoggingView.class);
public void doSomething(Item item) {
...
logger.info(loggingItemWriter.writeValueAsString(item));
...
}
}
I have a view with an HTML table, where the rows are created dynamically.
What is the best way to send the table data to the controller & update the model?
Currently, I have converted the table to JSON, as shown below:
{"0": ["Job Code","Hours","Rate","Expense"],"1": ["a1","b1","c1","d1"],"2": ["a2","b2","c2","d2"]}
I tried to use the newtonsoft JSON deserialize method, but it failed.
I think it failed to convert this format of JSON.
Is there any other method to serialize table that is accepted by newtonsoft JSON?
The {} in JSON tells the de-serializer that it is going to create an object. Keeping this in mind, the JSON you created would make a single object - with each property containing an array of strings. It seems like you want a list of objects, rather than this.
Are you using this method to de-serialize?
JsonConvert.DeserializeObject<T>(json)
You need to provide a type to de-serialize to. So in this case it would be:
class JobDetails
{
public string JobCode { get; set; }
public double Hours { get; set; }
public double Rate { get; set; }
public double Expense { get; set; }
}
In order to de-serialize to this type your JSON.NET JsonConvert call would look like:
[HttpPost]
public ActionResult Testing(string json)
{
var list = JsonConvert.DeserializeObject<List<JobDetails>>(json)
UPDATE_YOUR_MODEL_HERE(list);
return WHAT_YOU_WANT_TO_RETURN_HERE;
}
Your JSON would look like:
[
{
"JobCode": "a1",
"Hours": 37.5,
"Rate": 10,
"Expense": 375
},
{
"JobCode": "a2",
"Hours": 10,
"Rate": 20,
"Expense": 200
},
{
"JobCode": "a3",
"Hours": 37.5,
"Rate": 20,
"Expense": 750
}
]
This will convert the JSON into a List of JobDetails objects.
Alternatively if you want to resolve the JSON to objects as an Action parameter this should do the trick:
[HttpPost]
public ActionResult Testing(List<JobDetails> list)
{
UPDATE_YOUR_MODEL_HERE(list);
return WHAT_YOU_WANT_TO_RETURN_HERE;
}
If you're having trouble getting your table serialized to correct JSON try looking at this jQuery plug-in: https://github.com/lightswitch05/table-to-json