Im having an issue passing a nested list from my app to a web api. Im not seeing anything in my logs, but I keep receiving the following response in Postman:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-7d20c10b0a8b389f3ab9b13253d89356-96635c33db3d6d02-00",
"errors": {
"$": [
"The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
],
"RawJson": [
"The RawJson field is required."
]
}
}
When I input this JSON string in to the body/raw data with type set as JSON:
{
"RawJson": {
"ParkSys_ID": 3,
"Route_ID": 1,
"TrailName": "test",
"GPXData": [
{
"Timestamp": "2022-10-07T13:38:19.877 00:00",
"Latitude": 41.5263926,
"Longitude": -81.6508186,
"Altitude": 153.39999389648438,
"Accuracy": 20,
"VerticalAccuracy": 1.5861159563064575,
"Speed": null,
"Course": null,
"IsFromMockProvider": false,
"AltitudeReferenceSystem": 2
},
{
"Timestamp": "2022-10-07T13:38:21.379 00:00",
"Latitude": 41.5263905,
"Longitude": -81.6508169,
"Altitude": 153.39999389648438,
"Accuracy": 13.423999786376953,
"VerticalAccuracy": 1.2535593509674072,
"Speed": null,
"Course": null,
"IsFromMockProvider": false,
"AltitudeReferenceSystem": 2
},
{
"Timestamp": "2022-10-07T13:38:22.921 00:00",
"Latitude": 41.5263924,
"Longitude": -81.6508156,
"Altitude": 153.39999389648438,
"Accuracy": 13.423999786376953,
"VerticalAccuracy": 1.3553755283355713,
"Speed": null,
"Course": null,
"IsFromMockProvider": false,
"AltitudeReferenceSystem": 2
},
{
"Timestamp": "2022-10-07T13:38:24.479 00:00",
"Latitude": 41.5263905,
"Longitude": -81.6508186,
"Altitude": 153.39999389648438,
"Accuracy": 13.538000106811523,
"VerticalAccuracy": 1.3423168659210205,
"Speed": null,
"Course": null,
"IsFromMockProvider": false,
"AltitudeReferenceSystem": 2
},
{
"Timestamp": "2022-10-07T13:38:25.992 00:00",
"Latitude": 41.5263919,
"Longitude": -81.6508162,
"Altitude": 153.39999389648438,
"Accuracy": 14.10200023651123,
"VerticalAccuracy": 1.526308298110962,
"Speed": null,
"Course": null,
"IsFromMockProvider": false,
"AltitudeReferenceSystem": 2
}
]
}
}
This is my class that should be handling this, and I'm not sure why its not seeing the RawJson nested in the json
public class ActivitySubmissionService : IActivitySubmission
{
[JsonProperty("ParkSys_ID")]
public int ParkSys_ID { get; set; }
[JsonProperty("Route_ID")]
public int Route_ID { get; set; }
[JsonProperty("TrailName")]
public string TrailName { get; set; }
[JsonProperty("GPXData")]
public object GPXData { get; set; }
private readonly GPSAPIContext gpsapiContext;
public ActivitySubmissionService(GPSAPIContext gpsapiContext)
{
this.gpsapiContext = gpsapiContext;
}
//
// this is sent JSON string
public async Task<object> SubmitNewActivity(string RawJsonString)
{
var StringConvert = JsonConvert.DeserializeObject<List<ActivitySubmissionService>>(RawJsonString);
int Park_ID = StringConvert.Select(x => x.ParkSys_ID).FirstOrDefault();
int Route_ID = StringConvert.Select(x => x.Route_ID).FirstOrDefault();
string Trail_Name = StringConvert.Select(x => x.TrailName).FirstOrDefault();
object GPXData = StringConvert.Select(x => x.GPXData).ToList();
return GPXData;
}
}
CodePudding user response:
Ok so ASP.net does not support the text/plain
content type. The workaround would be to read the body ourselves. For this to work, the action method must not declare any parameters.
[HttpPost]
public async Task<IActionResult> ProcessJsonString()
{
using StreamReader sr = new StreamReader(Request.Body, System.Text.Encoding.UTF8);
string body = await sr.ReadToEndAsync();
// Now you can do whatever you need with the string, like parsing to JSON, although it is a weird approach.
return Ok(GetMyDataFromTheString(body));
}
CodePudding user response:
So I labored over this for a while...and it ended up being at least one obscure thing, followed by a mistake on my part.
I did not have
<Nullable>disable</Nullable>
Setup in my csproj file, which was causing the error
"RawJson": [
"The RawJson field is required."
]
Also, the way I was passing the [FromBody]
data to my SubmitNewActivity(string RawJsonString) {}
was completely wrong. I needed to pass it within my wrapper (which I hadn't even considered), and the nested element in my JSON was also requiring a wrapper class.
Another issue, which kind of totally threw me off, was that when passing the [FormBody]
data to your method, if you pass it via your wrapper, it will automatically deserialize the data. So this error:
"$": [
"The JSON value could not be converted to System.String. Path: $ | LineNumber: 0 | BytePositionInLine: 1."
],
Was a byproduct of
var StringConvert = JsonConvert.DeserializeObject<List<ActivitySubmissionService>>(RawJsonString);
Ultimately, the following code is what I ended up with:
public class GPXDataWrapper
{
public DateTime Timestamp { get; set; }
public decimal Latitude { get; set; }
public decimal Longitude { get; set; }
public decimal Altitude { get; set; }
public decimal Accuracy { get; set; }
public decimal VerticalAccuracy { get; set; }
public string Speed { get; set; }
public string Course { get; set; }
public bool IsFromMockProvider { get; set; }
public decimal AltitudeReferenceSystem { get; set; }
}
public class ActivitySubmissionWrapper
{
[JsonProperty("ParkSys_ID")]
public int ParkSys_ID { get; set; }
[JsonProperty("Route_ID")]
public int Route_ID { get; set; }
[JsonProperty("TrailName")]
public string TrailName { get; set; }
[JsonProperty("GPXData")]
public List<GPXDataWrapper> GPXData { get; set; } = new List<GPXDataWrapper>();
}
public async Task<string> SubmitNewActivity(ActivitySubmissionWrapper RawJsonString)
{
int Park_ID = RawJsonString.ParkSys_ID;
int Route_ID = RawJsonString.Route_ID;
string Trail_Name = RawJsonString.TrailName;
List<GPXDataWrapper> GPXData = RawJsonString.GPXData;
decimal Trail_Lat = GPXData[0].Latitude;
decimal Trail_Lon = GPXData[0].Longitude;
//
// start the insert by creating the trailhead
GPXTrailHead TrailHead_Insert = new GPXTrailHead();
TrailHead_Insert.TrailHead_Name = Trail_Name;
TrailHead_Insert.TrailHead_Lat = Trail_Lat;
TrailHead_Insert.TrailHead_Lon = Trail_Lon;
gpsapiContext.TrailHead.Add(TrailHead_Insert);
return "test: " Park_ID " , " Route_ID " ," Trail_Name " , " GPXData;
}