I am trying make a post with my form along with a file. I tried see others posts but without result. If I make the post without the file, works just fine. English is not my native language, sorry for any mistakes.
This is the error:
This is my data:
My code -> .Net
Model:
public class EmailDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Subject { get; set; }
public int? RecurrentDay { get; set; }
public DateTime? SendDate { get; set; }
public short SendHour { get; set; }
public int? GroupId { get; set; }
public int? RecipientTypeId { get; set; }
public int? ListTypeId { get; set; }
public bool SendForce { get; set; }
public string? Recipients { get; set; }
public bool Archived { get; set; }
public bool Active { get; set; }
public int IdTemplate { get; set; }
public TemplateDto Template { get; set; }
public int? IdRecurrence { get; set; }
public IFormFile? File { get; set; }
}
Controller:
[HttpPost("create")]
public async Task<ActionResult> CreateEmail([FromBody] EmailDto email)
Angular ->
Model:
export class EmailModel {
id!: number
idTemplate!: number
idRecurrence!: number
name!: string
subject!: string
active!: boolean
groupId!: number
recipientTypeId!: number
listTypeId! : number
recipients!: string
sendHour!: number
sendForce!: boolean
template!: TemplateModel
file!: File
}
email.component.ts:
onFileSelected(event: any) {
let fileList: FileList = event.target.files;
if (fileList.length > 0) {
this.file = fileList[0];
}
}
async onSubmit() {
if (this.file != null) {
this.email.file = this.file;
let headers = new HttpHeaders();
headers.append('Content-Type', 'multipart/form-data');
headers.append('Accept', 'application/json');
this.createEmail(this.email, headers);
} else {
this.createEmail(this.email);
}
}
createEmail(email: EmailModel, headers?: HttpHeaders) {
if (headers != null) {
this.EmailService.CreateEmail(email, headers).subscribe(() => {
this.isCreated = true;
}), (error: any) => {
console.log(error);
}
} else {
this.EmailService.CreateEmail(email).subscribe(() => {
this.isCreated = true;
}), (error: any) => {
console.log(error);
}
}
}
service:
CreateEmail(data: EmailModel, headers?: HttpHeaders) {
return this.http.post(`${this.emailApi}/create`, data, {headers: headers});
}
The last print:
If I understood your problem correctly, it would seem you are trying to send a JSON object body with a file and, on the .NET side, it can't deserialize it.
You could try to send the file as base64 instead of whatever you are attempting to send it as now:
async onSubmit()
{
if (this.file == null)
{
this.createEmail(this.email);
return;
}
// Create a file reader
const fileReader = new FileReader();
// Tell the file reader what to do once it loads
fileReader.onload = (event) =>
{
// which is to give us the base64 representation of the file
const base64File = event.target.result.toString();
// and assign it to the dto
this.email.file = base64File;
// before sending the request.
this.createEmail(this.email);
};
// Finally, read the file.
fileReader.readAsBinaryString(this.file);
}
Of course, you need to change file to a string in your DTOs.
You can then parse the base64 back to binary and do whatever you want with it. If you need IFormFile, then:
[HttpPost("create")]
public async Task<ActionResult> CreateEmail([FromBody] EmailDto email)
{
var byteArray = Convert.FromBase64String(email.File);
IFormFile file;
using(var memoryStream = new MemoryStream(byteArray))
{
file = new FormFile(memoryStream,
0,
byteArray.Length,
"someName",
"someFileName");
}
...
doWhatever
}
Related
On my API side, I use JsonConvert.SerializeObject() to set it to a string. This is the output from my API:
{"ContentType":null,"SerializerSettings":null,"StatusCode":null,"Value":{"draw":1,"recordsTotal":0,"recordsFiltered":0,"data":[],"order":null,"orderdir":null}}
This is what the API looks like:
[HttpPost("Preview")]
public JsonResult Preview([FromBody]AnnouncementAccessPreviewRequestViewModel svm)
{
ApiResponseViewModel arvm = new ApiResponseViewModel();
var res = announcementData.Preview(svm.SearchViewModel, svm.TenantId);
arvm.IsSuccessful = true;
arvm.Message = null;
arvm.Output = JsonConvert.SerializeObject(res);
return Json(arvm);
}
arvm.Output is a string
How can I only take the Value section from the output?
That's how I solved it when I had this problem.
private YourModel GetJsonObject()
var parsedObject = JObject.Parse(resultContent);
string p = parsedObject.ToString();
if (p.Contains("Succes"))
{
string popupJson = JObject.Parse(parsedObject["data"].ToString()).ToString();
YourModel= JsonConvert.DeserializeObject<YourModel>(popupJson);
return YourModel;
}
There is a side way to get this model from https://json2csharp.com/.
you can try this
var output=JObject.Parse(json);
Value value=output["Value"].ToObject<Value>();
class
public class Value
{
public int draw { get; set; }
public int recordsTotal { get; set; }
public int recordsFiltered { get; set; }
public List<object> data { get; set; }
public object order { get; set; }
public string orderdir { get; set; }
}
or if you can change Api
public JsonResult Preview([FromBody]AnnouncementAccessPreviewRequestViewModel svm)
{
var res = announcementData.Preview(svm.SearchViewModel, svm.TenantId);
var output = JsonConvert.SerializeObject(res);
return Json(output);
}
but the right way is
public IActionResult Preview([FromBody]AnnouncementAccessPreviewRequestViewModel svm)
{
return announcementData.Preview(svm.SearchViewModel, svm.TenantId);
}
in this case you don't need to serialize or deserialize
I am trying to do a get all with an ASP.NET Core project that uses this firebase library and I can't seem to return the children nested in an object. I have 3 classes: Route, Via & Waypoints(Serves as a bridge for JSON Deserialization).
public class Route
{
public string Route_ID { get; set; }
public string Destination { get; set; }
public string Origin { get; set; }
public Waypoints Stops { get; set; }
public Route()
{
}
}
public class Via
{
public string Via_ID { get; set; }
public string Route_ID { get; set; }
public int Seq_Number { get; set; }
public string Coordonnees { get; set; }
public string Description { get; set; }
public Via()
{
}
}
public class Waypoints
{
public List<Via> Vias;
public Waypoints()
{
}
}
In my GET method I go Fetch everything from my Routes and want to return it as one JSON List containing all my routes along with their waypoints but it only returns an empty list of Waypoints:
[HttpGet]
public async Task<IEnumerable<Route>> Get()
{
List<Route> routes = (await firebaseClient
.Child("routes")
.OrderByKey()
.OnceAsync<Route>())
.Select(item =>
new Route
{
Route_ID = item.Key,
Origin = item.Object.Origin,
Destination = item.Object.Destination,
Waypoints = item.Object.Waypoints
}).ToList();
foreach (Route route in routes)
{
List<Via> vias = (await firebaseClient
.Child("routes")
.Child(route.Route_ID)
.Child("Waypoints")
.OrderByKey()
.OnceAsync<Via>())
.Select(waypoint =>
new Via
{
Via_ID = waypoint.Key,
Route_ID = waypoint.Object.Route_ID,
Coordonnees = waypoint.Object.Coordonnees,
Seq_Number = waypoint.Object.Seq_Number,
Description = waypoint.Object.Description
}).ToList();
if(vias.Count > 0)
{
route.Stops.Vias = vias;
}
}
return routes;
}
My data structure:
{
"routes" : {
"987321": {
"Destination": "13.13;-12.34",
"Origin": "12.12;-12.12",
"Route_ID": "987321",
"Waypoints": {
"4d5e6f": {
"coordonnees": "45.8;-74.7",
"description": "Description",
"route_id": "987321",
"seq_number": 2,
"via_id": "4d5e6f"
},
"111222": {
"coordonnees": "45.8;-74.7",
"description": "Description",
"route_id": "987321",
"seq_number": 1,
"via_id": "111222"
}
}
}
}
}
And finally my call result:
[
{
"route_ID": "987321",
"destination": "13.13;-12.34",
"origin": "12.12;-12.12",
"waypoints": {}
}
]
It seems the Deserializing doesn't go further than the first layer of children. Is there any solution to this?
Thanks to Rena's suggestion, I figured out that the problem was located in my Waypoints bridging class that was missing a { get; set; }
Here is the change that was made to my class:
public class Waypoints
{
public List<Via> Vias { get; set; }
public Waypoints()
{
}
}
for two days I have been trying to understand how to move this JSON to an object in C#. I read a lot of topics and tried several ways to solve my problem, but still haven't solved it.
My JSON looks like this (cropped).
{
"data": [{
"id": 5643793,
"title": "It's a Title",
"description": "It's a Description.",
"tags": "#tag1 #tag2 #tag3 #tag4",
"source_url": "https:\/\/p.dw.com\/p\/3geny",
"vote_count": 120,
"bury_count": 17,
"comments_count": 33,
"related_count": 0,
"date": "2020-08-10 09:43:32",
"author": {
"login": "lnwsk",
"color": 2,
"avatar": "https:\/\/www.api.page.com\/cdn\/c3397992\/lnwsk_MfQz8MEQb2,q150.jpg"
},
"preview": "https:\/\/www.api.page.com\/cdn\/c3397993\/link_1597045214DgzqxRGEmy2UlpPZwaWfhI,w104h74.jpg",
"plus18": false,
"status": "promoted",
"can_vote": true,
"is_hot": false
}],
"pagination": {
"next": "https:\/\/api.page.com\/links\/promoted\/appkey\/X*******4y\/page\/2\/"
}
}
As you can see, there is an "element within an element" here (eg author or pagination (eg pagination I would like to get rid of)) and that is what gives me the most problem.
Here is my class where I have all the code to read the API:
using Newtonsoft.JSON;
public class PageAPI
{
public class Product
{
public string[] title { get; set; }
public double[] description { get; set; }
public string[] tags { get; set; }
public string[] source_url { get; set; }
public string[] vote_count { get; set; }
public string[] bury_count { get; set; }
public string[] comments_count { get; set; }
public string[] related_count { get; set; }
public string[] date { get; set; }
}
public async Task<Product> GetDataAsync()
{
string url = "https://api.page.com/";
string apisign = "6*********c1fe49a23f19ad6b2";
string requestParams = "links/promoted/appkey/X*******y";
Product obj = null;
// HTTP GET.
using (var client = new HttpClient())
{
// Setting Base address.
client.BaseAddress = new Uri(url);
// Setting content type.
client.DefaultRequestHeaders.Add("apisign", apisign);
// Initialization.
HttpResponseMessage response = new HttpResponseMessage();
// HTTP GET
response = await client.GetAsync(requestParams).ConfigureAwait(false);
// Verification
if (response.IsSuccessStatusCode)
{
try
{
// Reading Response.
string result = response.Content.ReadAsStringAsync().Result;
obj = JsonConvert.DeserializeObject<Product>(result);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
MessageBox.Show(ex.Message);
}
}
else
{
obj = null;
}
}
return obj;
}
}
in the Form where I want to get data from the "PageAPI" class I have:
private async void Form1_LoadAsync(object sender, EventArgs e)
{
var task = api.GetMainAsync();
task.Wait();
var data = task.Result;
label1.Text = data.title[0];
}
And... this doesn't works - on label1.Text = data.title[0]; i get error PageAPI.Product.title.get returned null
Thanks for any help!
You are missing the Root class that has "data" and "pagination" properties. Create Root class and deserialize to it and then get the data you need. Also, your Product class will have only strings.. not string[].
public class RootObject
{
public List<Product> data { get; set; }
}
public class Product
{
public string title { get; set; }
public double description { get; set; }
public string tags { get; set; }
public string source_url { get; set; }
public string vote_count { get; set; }
public string bury_count { get; set; }
public string comments_count { get; set; }
public string related_count { get; set; }
public string date { get; set; }
}
// and deserialize it
var rootObj = JsonConvert.DeserializeObject<RootObject>(result);
obj = rootObj.data.FirstOrDefault();
data object is an array ... you can loop through it to work with All the items. In above example, i used FirstOrDefault() to get the first item from the object.
Also note that when you access this data, you would not access it via [0]. Simply use
label1.Text = data.title;
Side Note
If you want the pagination property as well, create another class to get the name from pagination object.
public class RootObject {
public List<Product> data {get;set;}
public Pagination pagination {get;set;}
}
public class Pagination {
public string next {get;set; }
}
and when you deserialize your json, you would access the pagination by using,
Console.WriteLine(rootObj.pagination.next); // prints the url
How to get All the Product Names displayed
This is how you would go about getting a list of All the titles in the data object.
foreach (var product in rootObj.data)
{
Console.WriteLine(product.title);
Console.WriteLine(product.description);
Console.WriteLine(product.vote_count); // etc.
}
// Or you can create a list of all the titles from the rootObj using LINQ
List<string> allTitles = rootObj.data.Select(x => x.title).ToList();
I am not sure what you intend to do with the data you get... so not sure how to explain that piece.. but above example should give you an idea on how to iterate through all the products in the data object.
I have the following model in asp.net core 2 web api:
public class MainVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
public int DepartmentID { get; set; }
public IFormFile Logo { get; set; }
public List<ChildVM> ListChilds { get; set; }
}
public class ChildVM
{
public string Name { get; set; }
public int SequenceNo { get; set; }
public int NoOfPrices { get; set; }
public IFormFile Image { get; set; }
}
and the endpoint:
[HttpPost]
[Consumes("multipart/form-data")]
public void Post([FromForm]MainVM data)
{
}
Angular6 service that I am using to post data from angular:
addData(mydata: MainVM, logo: File, myImages: File[]): Observable<MainVM> {
const formData: FormData = new FormData();
formData.append('Logo', logo, logo.name);
if (myImages && myImages.length > 0) {
for (var i = 0; i < myImages.length; i++) {
formData.append('ListChilds[].Image', myImages[i], myImages[i].name);
}
}
formData.append('data', new Blob([JSON.stringify(mydata)], { type: 'application/json' }));
console.log(formData);
return this.http.post<MainVM>('http://localhost:60458/api/mycontroller/', formData, httpOptions).pipe(
map((res) => { console.log('res'); return res; }),
catchError(this.handleError('lpc', null))
);
}
Now the problem I am facing is that it only receives Logo on web api, all other fields remains null.
I think I am missing something. Please guide me.
Thank you.
Finally I found the solution:
First of all I changed my models, I removed the following line from ChildVM:
public IFormFile Image { get; set; }
and added the following line in MainVM:
public List<IFormFile> ListImages{ get; set; }
I did it because I find out that there is some bug in Asp.net Core 2.2 that if we took IFormFile type in sub model the Asp.net Core 2.2 starts to hang (details are in the link below):
https://github.com/aspnet/AspNetCore/issues/4802
Now at client side I changed the models accordingly and changes in service are as follow:
addData(mydata: MainVM, logo: File, myImages: File[]): Observable<MainVM> {
const formData: FormData = new FormData();
formData.append('Logo', logo, logo.name);
this.buildFormData(formData, mydata, null);
if (myImages && myImages.length > 0) {
for (var i = 0; i < myImages.length; i++) {
formData.append('ListImages', myImages[i], myImages[i].name);
}
}
console.log(formData);
return this.http.post<MainVM>('http://localhost:60458/api/mycontroller/', formData, httpOptions).pipe(
map((res) => { console.log('res'); return res; }),
catchError(this.handleError('lpc', null))
);
}
buildFormData(formData, data, parentKey) {
if (data && typeof data === 'object' && !(data instanceof Date) && !(data instanceof File)) {
Object.keys(data).forEach(key => {
console.log(key);
this.buildFormData(formData, data[key], parentKey ? `${parentKey}[${key}]` : key);
});
}
else {
const value = data == null ? '' : data;
formData.append(parentKey, value);
}
}
Trying to get the result from a webservice call to return a Model. I eep getting the error:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'CI.Models.Schedule' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
public Schedule getCourseSchedule()
{
var obj = new
{
States = new[] { new { State = "MX" } },
Zip = "",
Miles = "",
PaginationStart = 1,
PaginationLimit = 3
};
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "apoplication/json";
var url = "http://192.168.1.198:15014/ShoppingCart2/CourseSchedule";
var json = JsonConvert.SerializeObject(obj);
byte[] data = Encoding.UTF8.GetBytes(json);
byte[] result = client.UploadData(url, data);
string returnjson = Encoding.UTF8.GetString(result);
Schedule sched = JsonConvert.DeserializeObject<Schedule>(returnjson);
return sched;
}
}
Schedule Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Globalization;
namespace CI.Models
{
public class Schedule
{
public IEnumerable<Course> Courses { get; set; }
}
public class Course
{
/*
JSON Data returned from web service:
{
"ProgramGroup":"MR",
"ProgramCode":"RM",
"EventCode":"20160901MXMR",
"FormalDate":"September 1-2, 2016",
"StartDate":"2016\/09\/01",
"Price":5,
"LocName":"WB Hotel",
"LocAddress":"Av. Speedy Gonzales 220",
"LocCity":"Monterrey",
"LocState":"MX",
"LocZipCode":null,
"LicenseeURL":null,
"AgendaURL":"NA",
"SeatsAreAvailable":"2",
"GeneralInfoHTML":"General Info goes here.",
"GateKeeperHTML":null,
"EventType":"SS",
"TotalCourses":3
}
*/
public string ProgramGroup { get; set; }
public string ProgramCode { get; set; }
public string EventCode { get; set; }
public string FormalDate { get { return FormalDate; } set { FormalDate = convertFormalDateToSpanish(value); } }
public string StartDate { get; set; }
public double Price { get; set; }
public string LocName { get; set; }
public string LocAddress { get; set; }
public string LocCity { get ; set; }
public string LocState { get; set; }
public string LocZipCode { get; set; }
public string LicenseeURL { get; set; }
public string AgendaURL { get { return AgendaURL; } set { AgendaURL = buildAgendaLink(value); } }
public string SeatsAreAvailable { get; set; }
public string GeneralInfoHTML { get; set; }
public string GateKeeperHTML { get; set; }
public string EventType { get; set; }
public int TotalCourses { get; set; }
public string convertFormalDateToSpanish(string val)
{
DateTime TheDate = DateTime.Parse(StartDate);
string[] FormalDate = val.Split(" ".ToCharArray());
CultureInfo ci = new CultureInfo("es-ES");
string _Date = FormalDate[1].Replace("-", " al ").Replace(",", "");
string _Month = ci.TextInfo.ToTitleCase(TheDate.ToString("MMMM", ci));
val = string.Concat(_Date, " ", _Month);
return val;
}
private string buildAgendaLink(string val)
{
if (val.Trim() != "")
{
val = string.Concat("Agenda");
}
else
{
val = "Agenda";
}
return val;
}
}
}
Your server returns an array. Just try
Course[] courses = JsonConvert.DeserializeObject<Course[]>(returnjson);
Note that this is not an answer to your original problem, but I added it like an answer in order to explain my comment above with some actual code.
First problem with your code is that FormalDate and AgendaUrl properties simply won't work. Accessing them will result in a StackOverflowException, because you basically defined them recursively.
A property is merely syntax sugar for two separate getter/setter methods, so by writing this:
public class Course
{
public string FormalDate
{
get { return FormalDate; }
}
}
You are basically writing this:
public class Course
{
public string GetFormalDate()
{
// recursive call, with no terminating condition,
// will infinitely call itself until there is no
// more stack to store context data (and CLR
// will then throw an exception)
return GetFormalDate();
}
}
To fix that, you need to add an actual backing field, e.g.:
public class Course
{
private string _formalDate; // <-- this is a backing field;
// and this property uses the backing field to read/store data
public string FormalDate
{
get { return _formalDate; }
set { _formalDate = convertFormalDateToSpanish(value); }
}
}
Additionally, it's unusual for a property getter to return a different value than the one set through a setter. In other words, I would never expect this from a class:
var course = new Course();
course.StartDate = "2016/09/01";
course.FormalDate = "September 1-2, 2016";
Console.WriteLine(course.FormalDate); // prints "1 al 2 Septiembre" ?
I would rather move this functionality into a different class, or at least create different properties which return these values:
public class CourseInfo
{
// this is now a "dumb" auto-implemented property
// (no need for a backing field anymore)
public string FormalDate { get; set; }
// this read-only property returns the converted value
public string LocalizedFormalDate
{
get
{
return convertFormalDateToSpanish(FormalDate);
}
}
}