locked
Web service can't receive JSON from headers application/json RRS feed

  • Question

  • User-838640439 posted

    Hello,

    First of all, I prefer to specify that I've already search but I don't found anything to help with my problem.cry

    I need to create a web service that will receive JSON from TFS Hooks. (I do my test with Fiddler).

    in my code I have an User classes :

    namespace WebServiceProject
    {
        public class User
        {
            public User(string json)
            { 
                JObject jObject = JObject.Parse(json);
                JToken jRessource = jObject["resource"];
                JToken jRevision = jRessource["revision"];
                JToken jFields = jRevision["fields"];
                JToken jCreated = jFields["System.CreatedBy"];
                JToken jChanged = jFields["System.ChangedBy"];
    
                WorkItemType = (string)jFields["System.WorkItemType"];
                State = (string)jFields["System.State"];
                CreatedDate = (DateTime)jFields["System.CreatedDate"];
                CreatedBy = (string)jCreated["uniqueName"];
                ChangedDate = (DateTime)jFields["System.ChangedDate"];
                ChangedBy = (string)jChanged["uniqueName"];
                Title = (string)jFields["System.Title"];
            }
    
            public string WorkItemType { get; set; }
            public string State { get; set; }
            public DateTime CreatedDate { get; set; }
            public string CreatedBy { get; set; }
            public DateTime ChangedDate { get; set; }
            public string ChangedBy { get; set; }
            public string Title { get; set; }
            public string text { get; set; }
            public string html { get; set; }
            public string markdown { get; set; }
            public object displayName { get; set; }
            public string oldValue { get; set; }
            public string newValue { get; set; }
            public string href { get; set; }
        }
    }

    and my web service class : 

    namespace WebServiceProject
    {
        /// <summary>
        /// Description résumée de WebService1
        /// </summary>
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        [ScriptService]
        [System.ComponentModel.ToolboxItem(false)]
        // Pour autoriser l'appel de ce service Web depuis un script à l'aide d'ASP.NET AJAX, supprimez les marques de commentaire de la ligne suivante. 
        // [System.Web.Script.Services.ScriptService]
        public class WebService1 : System.Web.Services.WebService
        {
    
            [WebMethod]
            [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
            public string ReadJson(string json)
            {
                Console.WriteLine(json.Length);
                User user = new User(json);
    
                return "Title : " + user.Title + ", State : " + user.State + ", Type : " + user.WorkItemType + ", Created By : " + user.CreatedBy + ", Created Date : " + user.CreatedDate + ", Changed By : " + user.ChangedBy + ", Changed Date : " + user.ChangedDate + ".";
            }
        }
    }

    with this code I can receive JSON data from file with "application/x-www-form-urlencoded" headers, but when I change the content type with "application/json" it make me an error like this : 

    {"Message":"Primitive JSON non valide : json.","StackTrace":" à System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializePrimitiveObject()\r\n à System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth)\r\n à System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer)\r\n à System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)\r\n à System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input)\r\n à System.Web.Script.Services.RestHandler.GetRawParamsFromPostRequest(HttpContext context, JavaScriptSerializer serializer)\r\n à System.Web.Script.Services.RestHandler.GetRawParams(WebServiceMethodData methodData, HttpContext context)\r\n à System.Web.Script.Services.RestHandler.ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData)","ExceptionType":"System.ArgumentException"}

    I need to change the content type because in my JSON, I have some "&" which stop the reading of my JSON.

    Hope you can help me, and thank's to you for your time,

    Thursday, May 16, 2019 8:31 AM

Answers

  • User753101303 posted

    Hi,

    ASP.NET handles serialization/deserialization for you. So technically speaking the service you wrote expect a string object (ie it would work if the other side serialize an object to a string and send then this string (serializing it again) to your service, this is known sometimes as the "double serialization" issue).

    If you want to keep your current approach, I believe you have to delete the string parameter (so that ASP.NET doesn't try to deserialize a string from the incoming request) and read yourself directly the payload (I expect the Service class to expose an InputStream or something like that).

    The other option would be to just work with actual objects and let ASP.NET handle serialization/deserialization for you behind the scene.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, May 16, 2019 9:17 AM

All replies

  • User753101303 posted

    Hi,

    ASP.NET handles serialization/deserialization for you. So technically speaking the service you wrote expect a string object (ie it would work if the other side serialize an object to a string and send then this string (serializing it again) to your service, this is known sometimes as the "double serialization" issue).

    If you want to keep your current approach, I believe you have to delete the string parameter (so that ASP.NET doesn't try to deserialize a string from the incoming request) and read yourself directly the payload (I expect the Service class to expose an InputStream or something like that).

    The other option would be to just work with actual objects and let ASP.NET handle serialization/deserialization for you behind the scene.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, May 16, 2019 9:17 AM
  • User-838640439 posted

    Thanks for your reply Patrice,

    if I understood correctly, the paremeter that I asked in my web service try to serialize an object that is already serialize. 

    to solve ths problem, I need to use an InputStream (do you have a resource maybe?) OR to work with actual object that use ASP.NET to handle the serialization/deserialization.

    I think i'm gonna check the second solution at the moment. smile

    thanks again for your reply,

    Thursday, May 16, 2019 11:01 AM
  • User753101303 posted

    Yes, try rather to take advantage of ASP.NET features and to work directly with objects. Basically you should almost never have to write explicit serialization/deserialization code. 

    Yes ASP.NET is currently trying to parse the payload as a single string (as this is what expect your service) serialized to json (and doesn't care at all about the content of this string). The previous mime type allowed to read directly the payload to your string parameter so it seemed to work. Now the payload doesn't match any more the json syntax for a single string.

    Thursday, May 16, 2019 11:48 AM
  • User-838640439 posted

    I just changed my code like that : 

    User : 

    namespace WebServiceProject
    {
        public class User
        {
            public string WorkItemType { get; set; }
            public string State { get; set; }
            public DateTime CreatedDate { get; set; }
            public string CreatedBy { get; set; }
            public DateTime ChangedDate { get; set; }
            public string ChangedBy { get; set; }
            public string Title { get; set; }
            public string text { get; set; }
            public string html { get; set; }
            public string markdown { get; set; }
            public object displayName { get; set; }
            public string oldValue { get; set; }
            public string newValue { get; set; }
            public string href { get; set; }
        }
    }

    Web service : 

    namespace WebServiceProject
    {
        /// <summary>
        /// Description résumée de WebService1
        /// </summary>
        [WebService(Namespace = "http://tempuri.org/")]
        [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
        [ScriptService]
        [System.ComponentModel.ToolboxItem(false)]
        // Pour autoriser l'appel de ce service Web depuis un script à l'aide d'ASP.NET AJAX, supprimez les marques de commentaire de la ligne suivante. 
        // [System.Web.Script.Services.ScriptService]
    
        public class WebService1 : System.Web.Services.WebService
        {
            [WebMethod]
            [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
            public string ReadJson(string json)
            {
                JavaScriptSerializer js = new JavaScriptSerializer();
                User userObject = js.Deserialize<User>(json);
                string title = userObject.Title;
                string state = userObject.State;
                string workItemType = userObject.WorkItemType;
                string createdBy = userObject.CreatedBy;
                string changedBy = userObject.ChangedBy;
                DateTime createDate = userObject.ChangedDate;
                DateTime changedDate = userObject.ChangedDate;
    
                return title + ", " + state + ", " + workItemType + ", " + createdBy + ", " + changedBy + ", " + createDate + ", " + changedDate;
            }
        }
    }

    but it don't affect the error, I've got always the same one.

    (i don't find how to post a picture) but on fiddler I have this in the headers : 

    Host: localhost:60755
    Connection: keep-alive
    Content-Length: 4541
    Origin: http://localhost:60755
    Content-Type: application/json;
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
    Referer: http://localhost:60755/WebService1.asmx?op=ReadJson
    Accept-Encoding: gzip, deflate, br

    and in the body just a JSON like this : 

    {"key":value, "key2":"value2"}

    I understood that the problem comes from my parameter that is a single string and don't match with JSON but I don't know with what replace it, an Array, a List? or maybe nothing?

    thanks you again,

    Thursday, May 16, 2019 12:10 PM
  • User753101303 posted

    Just use the actual object(s) you expect ie for example something such as :

     public MyResult ReadJson(User user)

    And then a User object is created and deserialized from the payload for you and your method is called with this object. And so right away on the very first line of code, you could test user.WorkItemType or whatever your code needs to do.

    Similarly create and return the object you want from your method and the caller will serialize this object for you to the http response.

    The main point is to make sure your object match. You have sites allowing to paste a json sample and have the corresponding C# class being created for you....

    Thursday, May 16, 2019 12:49 PM
  • User-838640439 posted

    for those who may want the solution, i solved my problem with the inputStream just like this :

    string notification = "";
                using (var stream = new MemoryStream())
                {
                    var request = HttpContext.Current.Request;
                    request.InputStream.Seek(0, SeekOrigin.Begin);
                    request.InputStream.CopyTo(stream);
                    notification = Encoding.UTF8.GetString(stream.ToArray());
                }

    then I don't need parameter in my class.

    Thanks to you !

    Thursday, May 16, 2019 12:49 PM