Deserializing JSON objects as List<type> not working with asmx service - asp.net

I am having trouble deserializing my JSON string. I have a class of type person with public properties for sequence number of type int, first name, and last name. I want to pass an array of these objects in JSON format and have them deserialized as a list so I can loop through them on the server, but ASP.NET says something about not being supported to be deserialized as an array. I have validated the JSON I am producing, and it is valid. Is there something special about the JSON that ASP.NET needs to have before it can deserialize? The funny thing is if I serialize a list<person> object to JSON it looks exactly like the JSON I am producing. I must be missing something... To clarify, I'm using the ASP.NET Ajax library to deserialize. This is what I get back from the web service:
{"Message":"Type \u0027System.Collections.Generic.IDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]\u0027 is not supported for deserialization of an array."
Actually unfortunately this doesn't seem to have anything to do with deserializing, it appears that you can't pass an array of JSON objects to an asmx web service. Am I correct? If you can't do that, is it possible to pass a collection of JSON objects to a web service and have them processed on the server with ASP.NET and C#?
Update:
OK, here is my code. Here is the person class:
public class person
{
public person()
{
//
// TODO: Add constructor logic here
//
}
public int seq
{
get;
set;
}
public string firstName
{
get;
set;
}
public string lastName
{
get;
set;
}
}
And here is my JSON string:
[{"seq":1,"firstName":"Chris","lastName":"Westbrook"},
{"seq":2,"firstName":"sayyl","lastName":"westbrook"}]
And here is the code I'm using
[WebMethod]
public void updatePeople(string json)
{
IList<person> people =
new JavaScriptSerializer().Deserialize<IList<person>>(json);
//do stuff...
}

I figured it out. I wasn't wrapping my JSON in an object like ASP.NET Ajax requires. For future viewers of this question, all JSON objects must be wrapped with a main object before being sent to the web service. The easiest way to do this is to create the object in JavaScript and use something like json2.js to stringify it. Also, if using an asmx web service, the objects must have a __type attribute to be serialized properly. An example of this might be:
var person=new object;
person.firstName="chris";
person.lastName="Westbrook";
person.seq=-1;
var data=new object;
data.p=person;
JSON.stringify(data);
This will create an object called p that will wrap a person object. This can then be linked to a parameter p in the web service. Lists of type person are made similarly, accept using an array of persons instead of just a single object. I hope this helps someone.

Could you show the JSON string you are trying to deserialize and the way you are using the Deserialize method? The following works fine:
using System;
using System.Collections.Generic;
using System.Web.Script.Serialization;
namespace Test
{
class Program
{
class Person
{
public int SequenceNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public static void Main()
{
string json = "[{\"SequenceNumber\":1,\"FirstName\":\"FN1\",\"LastName\":\"LN1\"},{\"SequenceNumber\":2,\"FirstName\":\"FN2\",\"LastName\":\"LN2\"}]";
IList<Person> persons = new JavaScriptSerializer()
.Deserialize<IList<Person>>(json);
Console.WriteLine(persons.Count);
}
}
}

Or even simpler, when you are doing the $.ajax(...) use
data:"{\"key\":"+JSON.stringify(json_array)+"}",
and then on the other side of the code, make your function use the parameter "object key"
[WebMethod]
public static return_type myfunction(object key){...}

SERVER SIDE
[WebMethod]
public void updatePeople(object json)
CLIENT SIDE
var person = "[{"seq":1,"firstName":"Chris","lastName":"Westbrook"}
,{"seq":2,"firstName":"sayyl","lastName":"westbrook"}]";
var str = "{'json':'" + JSON.stringify(person) + "'}";

I think the problem is what type you have to deserialize. You are trying to deserialize type
IList
but you should try to deserialize just
List
Since interface can not be instantiated this might is the root problem.

Related

What is the default parameter type in .net core web api?

For example, I have an API like this:
[HttpPost("device")]
public string Post(string uid, string name)
{
return "value";
}
Will this code I posted work? By default?
form-data or x-www-form-urlencoded or raw?
I know If I want to post json data I have to add the [FromBody] and the parameter into a class right?
Question 2: how to post json data to the API, but do not have to use a instance of the parameter but can use the parameter?
string uid, string deviceId
Q1: I newly created an asp.net core 3.1 api project and create a new HomeController, and it worked in my side
using Microsoft.AspNetCore.Mvc;
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
[HttpPost("device")]
public string Post(string uid, string name)
{
return "value";
}
}
}
Q2: I think there might be some way to realize your goal but I'm afraid that needs more configuration such as adding a middleware. So I'm afraid the easiest way to receive json data is using an entity which contains the parameters. And if you consists on not creating entity for that data format, maybe you can use Dictionary like code below:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace WebApplication1.Controllers
{
public class HomeController : Controller
{
[HttpPost("device")]
public string Post([FromBody] Dictionary<string, string> test)
{
string uid = test.GetValueOrDefault("uid");
string name = test.GetValueOrDefault("name");
return "value";
}
}
}

How to return serialized JSON string, without further serialization from ASP.Net Core API

This question is similar to Return "raw" json in ASP.NET Core 2.0 Web Api but slightly more complicated.
I have a mixed content, some class like:
public class ResponseModel
{
public Guid Id { get; set; }
public DateTime TimesStamp { get; set; }
// this is actually JSON serialized data,
// which the function just passes through and doesn't need to understand
public string Data { get; set; }
}
Currently, a response would contain the Id and TimeStamp serialized correctly and Data would just be a string which would need to be deserialized one more time.
I'd instead want Data to be just pointing to the "Raw" json string, which I set it to, without further escaping it.
We don't make use of content negotiation, we only support JSON request and response, so this would be fine.
I know that I could deserialize the json string into a dynamic object and that would work, but why should the string be deserialized just to be serialized again?
So what I would want is something like
public class ResponseModel
{
public Guid Id { get; set; }
public DateTime TimesStamp { get; set; }
public object Data { get; set; }
}
but without the need to spend unnecessary time to deserialize and again serialize the content of the json string.
Not possible. If it's a string, you know it's JSON, but the serializer has no way of knowing that. However, even if it could somehow determine that it's a JSON string, it would still need to internally deserialize it so it could work it into the rest of the object, before serializing the whole thing - effectively no different than doing it yourself.

Make an XML element mandatory

Here I am currently working on a program that will serialize an XML file asp.net object. My problem is that I can not find the attribute that makes it mandatory to have a tag in the XML file.
You will find below the definition of my object.
[System.SerializableAttribute()]
public class EchangeIdentification
{
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("agrement")]
public string Agrement{ get; set; }
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("cvi")]
public string NumeroCvi { get; set; }
/// <remarks/>
[Required]
[XmlElement("siret")]
public string Siret { get; set; }
}
As far as I know, there is no way to declaratively force elements and attributes to be required using the XmlSerializer. C# object properties that can be null are always optional.
A few observations
[Serializable] is not used by the XML Serializer.
There is no way to make it required using the XML Serializer, but if you don't have to use XmlSerializer? DataContractSerializer provides the following option:
[DataMember(IsRequired = true)]
You don't need the "Attribute" name in the code, your code could look like this
[Serializable]
public class EchangeIdentification
{
[XmlElement("agrement")]
public string Agrement{ get; set; }
[XmlElement("cvi")]
public string NumeroCvi { get; set; }
[XmlElement("siret")]
public string Siret { get; set; }
}
Define "serialize an XML file asp.net object" and "makes it mandatory to have a tag in the XML". It all depends on how you're using this class.
Are you using it as a deserialization container, into which you will deserialize XML you receive? Then create an XSD schema, and validate the incoming XML before (or rather during) serialization. See Validating an XML against referenced XSD in C#.
On the other hand, if the user of this code is assigning properties of an instance of this class at runtime, and you serialize it through XmlSerializer, you could validate the output after serializing. See the linked question above, and Can I fail to deserialize with XmlSerializer in C# if an element is not found?.
Alternatively, you could implement serialization callbacks and create a validation method that throws an exception if [Required] properties have the default value for their type.
I'd go with the XSD route either way.

ServiceStack DTO Model Binding for Route Parameters AND Body

I have a Request DTO set up for performing a PUT against a service that results in an update.
I require both route parameters AND a json payload to be sent as the PUT (this payload is the ApprovalRoleData object below, and represents the new state of the object I want to have reflected on the server):
[Route("/qms/{QAID}/reviewers/{RoleType}", "PUT")]
public class UpdateReviewer
{
public string QAID { get; set; }
public string RoleType { get; set; }
public ApprovalRoleData UpdatedRoleData { get; set; }
}
Within my service, I have a Put() call that accepts this DTO: The issue is that the ApprovalRoleData object is not being deserialized (but the QAID and RoleType are):
public object Put(UpdateReviewer request)
{
string QAID = request.QAID; //can see value
string RT = request.RoleType; //can see value
ApprovalRoleData ard = request.UpdatedRoleData; //null
}
Is there a way like in WebAPI to specify that I want model binding to work with both route parameters AND a body?
Side Note:
Also, getting the underlying stream so I can just parse myself with base.RequestContext.Get<IHttpRequest>().InputStream didn't work since there was no remaining stream to read (i'm assuming the part of ServiceStack that does the model binding probably consumed the stream by the time I got to it?)

Custom JsonSerialization

I have a simple class in asp.net mvc that looks like this:
public class JsonResponseItem
{
public string Key { get; set; }
public string Value { get; set; }
public JsonResponseItem(string key, string value)
{
Key = key;
Value = value;
}
}
In my controllers I create a list of that type
List<JsonResponseItem> response = new List<JsonResponseItem>();
so I can easily manage and add to the Json response. A dictionary object is kind of hard to do that with.
When I return the json object
return Json(response);
It deserializes it so I have to reference everything by index first, because of the list. So if I had a property called "IsValid" I would have to reference it like this "IsValid[0]". I have way too much javascript code to make these changes.
How could I deserialize the JsonResponseItem class so I don't need the index reference in there?
Thanks!
A Dictionary<string, string> would serialize into exactly the Json you're asking for. If you don't want to expose directly a dictionary, wrap it around in another class or use Json(response.ToDictionary(item => item.Key).

Resources