How to deserialize a json string with C# ASP MVC - c#

I have a ASP MVC project with some Ajax Calls, where I am trying to pass from jquery/Ajax to my AjaxController (see bellow code) where in the controller sting item is receiving a json string like this
"[\n \"1002\",\n \"1003\"\n]"
And I get this error in my controller (See bellow code where the comment identifies the error)
Newtonsoft.Json.JsonSerializationException: Error converting value "1002" to type 'System.String[]'. Path '[0]', line 2, position 9. ---> System.ArgumentException: Could not cast or convert from System.String to System.String[]. at Newtonsoft.Json.Utilities.ConvertUtils.EnsureTypeAssignable(Object value, Type initialType, Type targetType) at Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(Object initialValue, CultureInfo culture, Type targetType) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType(JsonReader reader, Object value, CultureInfo culture, JsonContract contract, Type targetType) ...(continue)
This is my Json/Jquery creation function
function SaveObservations() {
var SerSel = $("#ServiceIdSelect2").val();
var obs = $("#observation").val();
var itemsX = [];
if (obs == "") {
mostrar_alert_ui("Validation message", "Observation cannot be null", 350);
} else {
//Start json creation
$("#ItemsPieces > input:checked").each(function () {
var id = $(this).val();
itemsX.push(id);
});
var itemsY = JSON.stringify(itemsX, null, 2);
//End json creation
Funciones_Ajax_conAlert("/Ajax/SendEmailItemsToClient/", { proyecto: SerSel, items: itemsY, observation: obs }, CloseObservations);
}
}
And here is my controller where the error is given
public JsonResult SendEmailItemsToClient(int proyecto, string items, string observation)
{
object data = null;
try
{
List<string[]> datax1 = JsonConvert.DeserializeObject<List<string[]>>(items); //Here is the issue Aqui tengo el problema
foreach (var item in datax1)
{
string test = item.ToString();
}
string mensaje = "";
int ProjectIdx = proyecto;
bool resp = CorreccionesController.SendItemsDifferentsToClient(ProjectIdx, mensaje);
if (resp) {
data = new
{
success = true,
titulo = "Notification",
mensaje = "A message explaining why the different items selected are used had been sent"
};
}
else
{
data = new
{
success = false,
titulo = "Notification",
mensaje = "The observation is Saved but the email couldn't be send, please contact support"
};
}
}
catch (Exception ex)
{
data = new
{
success = false,
titulo = "ERROR",
mensaje = ex.ToString()
};
}
return Json(data, JsonRequestBehavior.AllowGet);
}
The question would be how can I itterate that json string without receiving an error?

Your code need a little bit of refactoring; basically you have a json structure like this one:
[
"1002",
"1003"
]
That is basically an Array of Strings.
In your Controller you have the following line:
List<string[]> datax1 = JsonConvert.DeserializeObject<List<string[]>>(items);
Now, what this little chunk of code List<string[]> means? With that line you are trying to create a List of arrays of string, something like this:
[
["1002","1003"],
["1002","1003"]
]
So your deserializing methods fails with the following message: Could not cast or convert from System.String to System.String[]. Now makes sense.
So if you want to deserialize an json array of string you just needs:
List<string> datax1 = JsonConvert.DeserializeObject<List<string>>(items);
List<string> is just like and array of string(internally a list is constructed on an array basis, check this answer for more information about array and list: Array versus List<T>: When to use which?
Based on that info you can write your code this way too:
string[] datax1 = JsonConvert.DeserializeObject<string[]>(items); //Not tested, but should works.

Don't double Json encode, and let the WebAPI do all the work:
function SaveObservations() {
var SerSel = $("#ServiceIdSelect2").val();
var obs = $("#observation").val();
var itemsX = [];
if (obs == "") {
mostrar_alert_ui("Validation message", "Observation cannot be null", 350);
} else {
//Start json creation
$("#ItemsPieces > input:checked").each(function () {
var id = $(this).val();
itemsX.push(id);
});
//End json creation
Funciones_Ajax_conAlert("/Ajax/SendEmailItemsToClient/", { proyecto: SerSel, items: itemsX, observation: obs }, CloseObservations);
}
}
and
public JsonResult SendEmailItemsToClient(int proyecto, string[] items, string observation)
{
object data = null;
try
{
foreach (var item in items)
{
string test = item.ToString();
}
string mensaje = "";
int ProjectIdx = proyecto;
bool resp = CorreccionesController.SendItemsDifferentsToClient(ProjectIdx, mensaje);
if (resp) {
data = new
{
success = true,
titulo = "Notification",
mensaje = "A message explaining why the different items selected are used had been sent"
};
}
else
{
data = new
{
success = false,
titulo = "Notification",
mensaje = "The observation is Saved but the email couldn't be send, please contact support"
};
}
}
catch (Exception ex)
{
data = new
{
success = false,
titulo = "ERROR",
mensaje = ex.ToString()
};
}
return Json(data, JsonRequestBehavior.AllowGet);
}

Related

How to have an AWS Lambda/Rekognition Function return an array of object keys

This feels like a simple question and I feel like I am overthinking it. I am doing an AWS project that will compare face(s) on an image to a database (s3bucket) of other faces. So far, I have a lambda function for the comparefacerequest, a class library which invokes the function, and an UWP that inputs the image file and outputs a result. It has worked so far being based on boolean (true or false) functions, but now I want it to instead return what face(s) are recognized via an array. I struggling at implementing this.
Below is my lambda function. I have adjusted the task to be an Array instead of a bool and changed the return to be an array. At the bottom, I have created a global variable class with a testing array so I could attempt to reference the array elsewhere.
public class Function
{
//Function
public async Task<Array> FunctionHandler(string input, ILambdaContext context)
{
//number of matched faces
int matched = 0;
//Client setup
var rekognitionclient = new AmazonRekognitionClient();
var s3client = new AmazonS3Client();
//Create list of target images
ListObjectsRequest list = new ListObjectsRequest
{
BucketName = "bucket2"
};
ListObjectsResponse listre = await s3client.ListObjectsAsync(list);
//loop of list
foreach (Amazon.S3.Model.S3Object obj in listre.S3Objects)
{
//face request with input and obj.key images
var comparefacesrequest = new CompareFacesRequest
{
SourceImage = new Image
{
S3Object = new S3Objects
{
Bucket = "bucket1",
Name = input
}
},
TargetImage = new Image
{
S3Object = new S3Objects
{
Bucket = "bucket2",
Name = obj.Key
}
},
};
//compare with confidence of 95 (subject to change) to current target image
var detectresponse = await rekognitionclient.CompareFacesAsync(comparefacesrequest);
detectresponse.FaceMatches.ForEach(match =>
{
ComparedFace face = match.Face;
if (match.Similarity > 95)
{
//if face detected, raise matched
matched++;
for(int i = 0; i < Globaltest.testingarray.Length; i++)
{
if (Globaltest.testingarray[i] == "test")
{
Globaltest.testingarray[i] = obj.Key;
}
}
}
});
}
//Return true or false depending on if it is matched
if (matched > 0)
{
return Globaltest.testingarray;
}
return Globaltest.testingarray;
}
}
public static class Globaltest
{
public static string[] testingarray = { "test", "test", "test" };
}
Next, is my invoke request in my class library. It has so far been based on the lambda outputting a boolean result, but I thought, "hey, it is parsing the result, it should be fine, right"? I do convert the result to a string, as there is no GetArray, from what I know.
public async Task<bool> IsFace(string filePath, string fileName)
{
await UploadS3(filePath, fileName);
AmazonLambdaClient client = new AmazonLambdaClient(accessKey, secretKey, Amazon.RegionEndpoint.USWest2);
InvokeRequest ir = new InvokeRequest();
ir.InvocationType = InvocationType.RequestResponse;
ir.FunctionName = "ImageTesting";
ir.Payload = "\"" + fileName + "\"";
var result = await client.InvokeAsync(ir);
var strResponse = Encoding.ASCII.GetString(result.Payload.ToArray());
if (bool.TryParse(strResponse, out bool result2))
{
return result2;
}
return false;
}
Finally, here is the section of my UWP where I perform the function. I am referencing the lambda client via "using Lambdaclienttest" (name of lamda project, and this is its only instance I use the reference though). When I run my project, I do still get a face detected when it should, but the Globaltest.testingarray[0] is still equal to "test".
var Facedetector = new FaceDetector(Credentials.accesskey, Credentials.secretkey);
try
{
var result = await Facedetector.IsFace(filepath, filename);
if (result)
{
textBox1.Text = "There is a face detected";
textBox2.Text = Globaltest.testingarray[0];
}
else
{
textBox1.Text = "Try Again";
}
}
catch
{
textBox1.Text = "Please use a photo";
}
Does anyone have any suggestions?

Sendgrid dynamic data not being sent to Template

This is how I send my email:
public async System.Threading.Tasks.Task<string> SendInternalEmail(BasicEmailStructureViewModel Structure, List<string> AdditionalVariables, TenantEmailTemplate tenantEmailTemplate, TenantCommunication tenantCommunication, string ReceiverId, string ReceiverEmail, string ReceiverName, string CampaignName)
{
try
{
var client = new SendGridClient(tenantCommunication.SendgridApiKey);
var message = new SendGridMessage();
message.SetFrom(new EmailAddress(tenantCommunication.SendgridPrimarySender, tenantCommunication.SendgridPrimarySenderTag));
message.AddTo(new EmailAddress(ReceiverEmail, $"{ReceiverName}"));
message.Subject = tenantEmailTemplate.Subject;
message.SetTemplateId(tenantEmailTemplate.TemplateId);
List<string> jsonVars = new List<string>();
var subjectString = #$"""subject"":""{tenantEmailTemplate.Subject}""";
jsonVars.Add(subjectString);
foreach (PropertyInfo prop in Structure.GetType().GetProperties())
{
var variableString = #$"""{prop.Name}"":""{prop.GetValue(Structure, null)}""";
}
for (var i = 0; i < AdditionalVariables.Count; i++)
{
jsonVars.Add(AdditionalVariables[i]);
}
var flattenList = "{" + string.Join(",", jsonVars) + "}";
var emailData = Newtonsoft.Json.JsonConvert.DeserializeObject<object>(flattenList);
message.SetTemplateData(emailData);
if (CampaignName != null && CampaignName != "")
{
message.AddCustomArg("CampaignName", CampaignName);
}
var response = await client.SendEmailAsync(message);
if (response.IsSuccessStatusCode == true)
{
return Guid.NewGuid().ToString();
}
else
{
var errorMessage = response.Body.ReadAsStringAsync().Result;
return errorMessage;
}
}
catch(Exception e)
{
if (e != null)
{
return e.Message;
}
}
return "Invalid Email";
}
A typical input to this function will be like this:
var variableString =
#$"""verification_link"":""www.website.com?Key={Input.Key}""";
My email sends normally, however, none of the variables that I have set have been sent through. This is based roughly off the template sample on github: https://github.com/sendgrid/sendgrid-csharp/blob/main/examples/templates/templates.cs
Is there another sample I can use or what is the correct way to send variables dynamically?
I don't think that is the best way to construct the JSON for your dynamic template variables.
Would it be possible for you to build the variables as an object and then serialize them. Like:
var templateData = new {
subject = tenantEmailTemplate.Subjectm
otherVariableName = otherVariable
};
string emailData = JsonConvert.SerializeObject(templateData);
message.SetTemplateData(emailData);

Writing mutation graphql-client c#

I tried to write mutation but it gives me error.
as {"errors":[{"message":"Syntax Error: Expected $, found Name \"objects\"","locations":[{"line":2,"column":27}],"extensions":{"code":"GRAPHQL_PARSE_FAILED"}}]}
The code I wrote is this.
[HttpGet("GraphDataCreate")]
public async Task<dynamic> ReturnGraphDataCreate()
{
using var graphQLClient = new GraphQLHttpClient("https://api.spacex.land/graphql/", new NewtonsoftJsonSerializer());
var trial = new GraphQLRequest
{
Query = #"
query insert_users(objects: { name: $name, rocket: $rocket }) {
returning {
id
name
rocket
timestamp
twitter
}
}",
OperationName = "insert_users",
Variables = new
{
name = "AdiAntNam",
rocket = "SPUTNIK5V"
}
};
var graphQLResponse = (object)(null);
try
{
graphQLResponse = await graphQLClient.SendQueryAsync<dynamic>(trial);
}
catch (Exception ex)
{
var err = ex;
string err1 = ex.Message;
string err2 = ex.InnerException.Message;
}
return Task.FromResult(graphQLResponse);
}
Now what is it I'm missing in this part ?
The reference I've taken is from here.
https://github.com/graphql-dotnet/graphql-client
The problem with the example is the data type which is written that is hard to follow PersonAndFilms($id: ID) now ID is a data type so I was assuming that it was just a variable name declared that's why I was in confusion.
So I had written it as query insert_users(objects: { name: $name, rocket: $rocket }) which was not understandable for GraphQL as it requires Data Type, so I re-writed my query as below.
var trial = new GraphQLRequest
{
Query = #"
mutation xyz($nameU: String, $rocketU: String)
{
insert_users(objects: { name: $nameU , rocket: $rocketU })
{
returning
{
id
name
rocket
timestamp
twitter
}
}
}",
OperationName = "xyz",
Variables = new
{
nameU = "AdiAntNam2",
rocketU = "SPUTNIK5V"
}
};
Now the mutation xyz($nameU: String, $rocketU: String) it reads correct data type. The example is correct some what in the project but confusing to end user as $id: ID is not much understandable so its better to use example as mine.

Controller action that receives any data in xml json or form-urlencoded

I have a requirement to create a controller action that can accept any data in XML, JSON or form-urlencoded. However, I couldn't make it work.
If my action has a Dictionary<string, object> parameter, it works for JSON and XML data, but not for form-urlencoded. If my action has a FormDataCollection parameter, it works for form-urlencoded but not for JSON and XML.
[HttpPost]
public HttpResponseMessage Default(FormDataCollection data /*Dictionary<string,object> data*/)
{
try
{
if (data == null)
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = "The request body is empty" });
//var collection = GetCollection(data); //used when data=Dictionary<string,object>
var collection = data.ReadAsNameValueCollection();
var agent = new ScriptingAgentClient();
var parameters = ServiceAgentParameters.From(collection);
var result = agent.Run(parameters);
if (result.Error)
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = result.ErrorMessage, Exception = result.Exception });
if (result.Data != null && result.Data.Count == 1) //result.Data is byte[]
{
//TODO: use the right Serializer
var resultString = Encoding.UTF8.GetString(result.Data[0]);
var serializer = new JavaScriptSerializer();
var dict = serializer.Deserialize<Dictionary<string, string>>(resultString);
return Request.CreateResponse(HttpStatusCode.OK, dict);
}
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = "Unknown error" });
}
catch (Exception ex)
{
Logger.Error("Error handling request", ex);
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = ex.Unwrap().Message });
}
}
ScriptingAgentClient will handle the data, whatever it may be.
If this isn't possible, how can I create two actions with same route where one will handle XML/JSON and another will handle form-urlencoded?
Note that I'm using .net40 and I can't change that. Noteworthy to mention that this webapi is a self-hosted api that will run in a windows service.
I was able to make it work reading the Request instead of letting WebApi figuring it out:
[HttpPost]
public HttpResponseMessage Default()
{
try
{
NameValueCollection collection = Request.Content.IsFormData() ?
Request.Content.ReadAsFormDataAsync().Result :
GetCollection(Request.Content.ReadAsAsync<IDictionary<string, object>>().Result);
var parameters = ServiceAgentParameters.From(collection);
var agent = new ScriptingAgentClient();
var response = agent.Run(parameters);
if (response.Error)
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = response.ErrorMessage, Exception = response.Exception });
if (response.Data != null && response.Data.Count == 1) //result.Data is List<byte[]>
{
//TODO: use the right Serializer
var resultString = Encoding.UTF8.GetString(result.Data[0]);
var serializer = new JavaScriptSerializer();
var dict = serializer.Deserialize<Dictionary<string, string>>(resultString);
return Request.CreateResponse(HttpStatusCode.OK, dict);
}
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = "Unknown error" });
}
catch (Exception ex)
{
Logger.Error("Error handling request", ex);
return Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorMessage = ex.Unwrap().Message });
}
}

Unit Testing using Moq

This is a method under my controller which is used to create dummy keys to encrypt the data in the application and store the same in the amazon s3 bucket.
public JsonResult SaveMasterKeys(string MekText, int Thismek)
{
string folderName = string.Empty, fileName = string.Empty;
List<string> folderNameList = new List<string>();
folderNameList.Add("Guard1");
folderNameList.Add("Guard2");
try
{
if (Thismek == 1)
{
folderName = "Guard1";
fileName = "NewMek1.key";
}
else
{
folderName = "Guard2";
fileName = "NewMek2.key";
}
AWSS3File aws = new AWSS3File();
//aws.BucketExist(filePath);
//aws.CreateFile(MekText, filePath);
// Check Weather the Folder is exist or not
if (!aws.CheckFolderExist(folderName))
{
foreach (var item in folderNameList)
{
aws.CreateFolder(item);
if (item == "Guard1")
{
aws.CreateFileIntoS3((item == folderName ? MekText : ""), item, "NewMek1.key");
aws.CreateFileIntoS3("", item, "Mek1.key");
}
else
{
aws.CreateFileIntoS3((item == folderName ? MekText : ""), item, "NewMek2.key");
aws.CreateFileIntoS3("", item, "Mek2.key");
}
}
}
else
{
aws.CreateFileIntoS3(MekText, folderName, fileName);
}
ViewData["SaveMessage"] = "Saved successfully.";
}
catch (Exception ex)
{
XTP.Logger.LogCritical("XTP.Web.internaltools", ex.ToString());
ViewData["SaveMessage"] = "Keys not updated successfully.";
}
return Json(new { success = true, value = ViewData["SaveMessage"] }, JsonRequestBehavior.AllowGet);
}
And this is the TESTMETHOD I have written for the same
[TestMethod]
public void MockAlways()
{
var mock = new Mock<AccountController>();
JsonResult json = new JsonResult();
//new { success = true, value = ViewData["SaveMessage"] }, JsonRequestBehavior.AllowGet
json.Data = new { success = true, value = "sa" };
json.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
mock.Setup(x => x.SaveMasterKeys("ss", 1)).Returns(json);
var controller = new AccountController();
var result = controller.SaveMasterKeys("ss", 1) as JsonResult;
Assert.AreEqual(mock.Object.SaveMasterKeys("ssxs", 1), result.Data.ToString());
}
I am getting an invalid setup error. Is there a way to resolve this error?
I think that you misunderstood how to mock a controller's action. You are mocking it and then comparing it with the mocked controller. This is not a way to go(it is like checking whether true == true.
Basically this error means that Moq cannot override non-virtual member(it is self-explanatory). You should change method's signature to virtual to allow overriding it.
But - you shouldn't mock action like this. Instead you should mock its dependencies(services, contexts, gateways etc.) and check whether with known input values you can get expected results without mocking the action itself.

Categories