Home > Blockchain >  sending nested data via json string POST to webapi c#
sending nested data via json string POST to webapi c#

Time:10-09

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;
    }
  • Related