I am trying to post some data to a WCF service I don't own. I am using XmlSerializer to format the message, which creates issues with data containing single quotes.
Having a node like this
...
<FirstName>D'Arcy</FirstName>
...
Causes service to throw "400 bad request" exception. If I manually construct request in Fiddler like this
...
<FirstName>D"Arcy</FirstName>
...
then it works just fine.
I am confused as to why would this be needed. Various online XML validators claim my original XML is valid, but WCF doesn't like it? Any way to fix/workaround?
Here is my code just in case:
static XmlSerializer RequestSerializer = new XmlSerializer(typeof(Message));
static XmlSerializer ResponseSerializer = new XmlSerializer(typeof(int), new XmlRootAttribute("int") { Namespace = "http://schemas.microsoft.com/2003/10/Serialization/" });
static XmlWriterSettings WriterSettings = new XmlWriterSettings { OmitXmlDeclaration = true, CloseOutput = false, Encoding = new UTF8Encoding(false) };
private static int PostData(Message msg)
{
var request = (HttpWebRequest)WebRequest.Create("https://...");
request.ContentType = "text/xml;charset=UTF-8";
request.Method = "POST";
using (var writer = XmlWriter.Create(request.GetRequestStream(), WriterSettings))
RequestSerializer.Serialize(writer, msg);
using (var response = (HttpWebResponse)request.GetResponse())
using (var responseStream = response.GetResponseStream())
{
return (int)ResponseSerializer.Deserialize(responseStream);
}
}
[XmlRoot("Order", Namespace = "http://...")]
public class Message
{
public string City;
public string Coupon;
public DateTime CreateDate;
public string Email;
public string FirstName;
public string Language;
public string LastName;
public int OrderID;
public string PostalCode;
public string ProductID;
public string Province;
public string StreetAddress1;
public string StreetAddress2;
}
Turns out this WCF service is inserting data into their DB by using straight up query (non-parametrized) and doesn't escape single quotes. It was really confusing too, since HTTP status 400 to me implies it was an issue with deserialization or something similar in the framework.
Sorry to bother everyone :(
Related
I have this JSON string:
[{"fkp_keyword":"CLI_RID"},
{"fkp_keyword":"DOC_NAME"},
{"fkp_keyword":"FILENAME"},
{"fkp_keyword":"PRINT_DATE"},
{"fkp_keyword":"EVENT_CODE"},
{"fkp_keyword":"CONFL_RID"},
{"fkp_keyword":"PROGRAM_CODE"},
{"fkp_keyword":"CES"},
{"fkp_keyword":"DISTR"},
{"fkp_keyword":"REC_DATE"},
{"fkp_keyword":"REC_RID"},
{"fkp_keyword":"PFL_RID"},
{"fkp_keyword":"DES"},
{"fkp_keyword":"CER_RID"}
]
I need to convert it into a List of the class kw below.
Definitions:
public class kw
{
public string fkp_keyword { get; set; }
}
But this code:
List<kw> header = new List<kw>();
using (WebClient client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
string result = client.DownloadString(parms);
header = JsonConvert.DeserializeObject<List<kw>>(result);
}
The call returns the JSON string above but when trying to convert it, the code above returns this exception:
Error converting value to type 'System.Collections.Generic.List[LA.Models.kw]
Update
I changed the definitions to this:
public class kwList
{
public kw[] Property1 { get; set; }
}
public class kw
{
public string fkp_keyword { get; set; }
}
and the code to this:
kwList header = new kwList();
using (WebClient client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
string result = client.DownloadString(parms);
header = JsonConvert.DeserializeObject<kwList>(result);
}
But now I'm getting this Exception:
Could not cast or convert from System.String to LicenseeArchive.Models.kwList.
What am I doing wrong?
For whatever reason, it appears that the JSON string returned by that URL is double-serialized. That is, it contains extra backslashes to escape all the quotes, which then prevents it from being deserialized properly to an array of objects. That is why you are getting an error.
To work around the problem, you can deserialize it twice: first to unescape the JSON, the second to do the "real" deserialization to your classes. Longer term, you may also wish to contact the provider of the API to see if they will fix their JSON.
List<kw> header = new List<kw>();
using (WebClient client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
string result = client.DownloadString(parms);
string unescapedJson = JsonConvert.DeserializeObject<string>(result);
header = JsonConvert.DeserializeObject<List<kw>>(unescapedJson);
}
Fiddle: https://dotnetfiddle.net/XEULdy
JSON string you provided can be loaded with your first class definition:
public class kw
{
public string fkp_keyword { get; set; }
}
Example:
string example = "[{\"fkp_keyword\":\"CLI_RID\"}, {\"fkp_keyword\":\"DOC_NAME\"}, {\"fkp_keyword\":\"FILENAME\"}]";
List<kw> kws = JsonConvert.DeserializeObject<List<kw>>(example);
Maybe you are not providing all details. Or your json string looks different.
I am trying to search all favourite Filters (JIRA) from the actual User using c# HttpWebRequest and Rest-Api. I am still able to read Issues but the filters aren't working.
Reading Issues works as follows:
For example I have this URL to get all Issues from project IT:
http://jira-test.myServer.de/rest/api/2/search?jql=project=%22IT%22
I am using DataContractJsonSerializer to swap the JSON Response to C#-Objects.
From this class I am getting an object after Serialization:
[DataContract]
internal class Kopf
{
[DataMember]
public string startAt = string.Empty;
[DataMember]
public string maxResults = string.Empty;
[DataMember]
public string total = string.Empty;
[DataMember]
public Issues[] issues = null;
}
The first lines of JSON are looking like this:
{"expand":"schema,names","startAt":0,"maxResults":50,"total":23044,"issues":[{"expand":"operations,editmeta,changelog,transitions,renderedFields","id":"40000","self":"http://jira-test.myServer.de/rest/api/2/issue/40000","key":"IT-23237","fields":
So I can't understand why the following isn't working for me:
This URL give me the right JSON in Browser:
http://jira-test.myServer.de/rest/api/2/filter/favourite
First lines of JSON:
[{"self":"http://jira-test.myServer.de/rest/api/2/filter/10119","id":"10119","name":"Aktiv","description":"Alle Aufgaben die gerade aktiv von mir bearbeitet werden.","owner":{"self":"http://jira-test.myServer.de/rest/api/2/user?username=sb9923","key":"sb9923","name":"sb9923","avatarUrls":{"16x16":"http://jira-test.myServer.de/secure/useravatar?
And here is my Object which I want to serialize:
[DataContract]
internal class FilterData
{
[DataMember]
public FilterKopf[] filter = null;
}
[DataContract]
internal class FilterKopf
{
[DataMember]
public string id = string.Empty;
[DataMember]
public string name = string.Empty;
[DataMember]
public string description = string.Empty;
[DataMember]
public string jql = string.Empty;
}
I don't get any Exception or something but the FilterKopf Array in the FilterData-Object is always null.
I hope someone can help me with this. I think my C#-Class is the problem because the JSON seems fine and my browser gives the right output.
If I understand right your problem is that the result contains an array of "Filter" objects but you want to deserialize it as a simple object containing the array. So all you need is to deserialize the stream as FilterKopf[] instead of FilterData.
I created a simple request based on this answer (I modified it slightly, e.g. not POST but GET)
public class JiraTest
{
internal IEnumerable<FilterKopf> GetFavouriteFilters()
{
string url = "http://jira-test.myserver.de/rest/api/2/filter/favourite";
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "GET";
httpWebRequest.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.Default.GetBytes("YOUR_USERNAME:YOUR_PASSWORD"));
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(FilterKopf[]));
var filterKoepfe = (FilterKopf[])serializer.ReadObject(httpResponse.GetResponseStream());
return filterKoepfe;
}
}
[DataContract]
internal class FilterKopf
{
[DataMember]
public string id = string.Empty;
[DataMember]
public string name = string.Empty;
[DataMember]
public string description = string.Empty;
[DataMember]
public string jql = string.Empty;
}
With my own account and with my access to our Jira server the results really reflected my favourite filters.
Update
As a second chance, try to use Json.NET instead of DataContractJsonSerializer. Add to the project through NuGet, and replace the two rows of deserialization to these:
FilterKopf[] filterKoepfe = null;
using (Stream stream = httpResponse.GetResponseStream())
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
{
string jsonResponse = reader.ReadToEnd();
filterKoepfe = Newtonsoft.Json.JsonConvert.DeserializeObject<FilterKopf[]>(jsonResponse);
}
Let's take a look what this does.
after long hours of searching for a good JSON library, i found Newtownsoft.json, so i started using it to decode a json text i get from a web request, i don't know if the json is being decoded properly
the class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//Request library
using System.Net;
using System.IO;
using Newtonsoft.Json;
namespace TestApplication
{
class Connect
{
public string id;
public string type;
private string api = "https://api.stackexchange.com/2.2/";
private string options = "?order=desc&sort=name&site=stackoverflow";
public object request()
{
string totalUrl = this.join(id);
string json = this.HttpGet(totalUrl);
return this.decodeJson(json);
}
private string join(string s)
{
return api + type + "/" + s + options;
}
private string HttpGet(string URI)
{
string html = string.Empty;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URI);
request.AutomaticDecompression = DecompressionMethods.GZip;
request.ContentType = "application/json; charset=utf-8";
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
html = reader.ReadToEnd();
}
return html;
}
private object decodeJson(string json)
{
object js = JsonConvert.DeserializeObject(json);
return js;
}
}
}
the class object is being accessed from the form in this way:
Connect rq = new
rq.id = usernameText.Text;
rq.type = "users";
Debug.WriteLine(rq.request());
i don't know why i can't do rq.request().items or rq.request()["items"], i am still learning c# and i would like to know how to access the json object members in the proper way.
NOTE: this is the first desktop program i'm developing, i am a php/nodejs developer and i wanted to make an application that will connect to stack exchange database and retrieve user's info.
The return type of your request method is object, and so the returned instance will not have a property named items.
You'll need to use generic methods and specify the correct type parameter.
Try changing your decodeJson method to this:
private T decodeJson<T>(string json)
{
var js = JsonConvert.DeserializeObject<T>(json);
return js;
}
And then change your request method to this:
public T request<T>()
{
string totalUrl = this.join(id);
string json = HttpGet(totalUrl);
return decodeJson<T>(json);
}
Now write a class with properties that match the name and type of the properties in the JSON returned from the web request.
Then specify the type of this new class as the type parameter for your call to the request method.
For example, if you expected the JSON to contain a string called 'Name' and an int called 'Age', write a class that is something like this:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
and then call request like this
Person myPerson = rq.request<Person>();
and you'll be left with an instance of Person with Name and Age properties
My code is below. When I post a JSON body that has \v in it, the server receives JSON that it cannot parse. Any help on how to make it work?
EDIT: well, the argument about my server not being able to parse it is weak, but http://jsonlint.com/ complains about it too. : )
in my emacs it looks like this:
{"key":"a^Kb"}
where ^K I think is a single character.
code:
using RestSharp;
using System;
using System.Collections.Generic;
namespace testapp
{
class Program
{
static void Main(string[] args)
{
var client = new RestClient();
var request = new RestRequest();
request.Method = Method.POST;
request.RequestFormat = DataFormat.Json;
request.Resource = "http://example.com/";
var data = new Dictionary<string, string>();
data.Add("key", "a\vb");
request.AddBody(data);
var response = client.Execute<Dictionary<string, string>>(request);
}
}
}
The default RestSharp JSON serializer serializes this as equivalent to the C# string "{\"key\":\"a\vb\"}"; that is, it sends the \v as a raw byte (0b) over the wire. This is why your server has trouble reading it. I'm unsure if this is legal JSON, but in any case there are ways to escape such a character to avoid issues.
I'd recommend using Json.NET to de/serialize your JSON. If you use the the following class (originally part of RestSharp) as your serializer, it will use Json.NET and should work correctly, since it will encode the \v as \u000b over the wire. The code (sans comments, and renamed for clarity) is copied here for reference:
public class JsonNetSerializer : ISerializer
{
private readonly Newtonsoft.Json.JsonSerializer _serializer;
public JsonNetSerializer()
{
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include
};
}
public JsonNetSerializer(Newtonsoft.Json.JsonSerializer serializer)
{
ContentType = "application/json";
_serializer = serializer;
}
public string Serialize(object obj)
{
using (var stringWriter = new StringWriter())
{
using (var jsonTextWriter = new JsonTextWriter(stringWriter))
{
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
var result = stringWriter.ToString();
return result;
}
}
}
public string DateFormat { get; set; }
public string RootElement { get; set; }
public string Namespace { get; set; }
public string ContentType { get; set; }
}
To use it, add this line:
request.JsonSerializer = new JsonNetSerializer();
And it will serialize like:
{
"key": "a\u000bb"
}
Which is perfectly valid according to JSONLint.
I'm trying to convert the result I get from my web service as a string and convert it to an object.
This is the string I'm getting from my service:
<StatusDocumentItem><DataUrl/><LastUpdated>2013-01-31T15:28:13.2847259Z</LastUpdated><Message>The processing of this task has started</Message><State>1</State><StateName>Started</StateName></StatusDocumentItem>
So I have a class for this as:
[XmlRoot]
public class StatusDocumentItem
{
[XmlElement]
public string DataUrl;
[XmlElement]
public string LastUpdated;
[XmlElement]
public string Message;
[XmlElement]
public int State;
[XmlElement]
public string StateName;
}
And this is how I'm trying to get that string as an object of type StatusDocumentItem with XMLDeserializer (NB. operationXML contains the string):
string operationXML = webRequest.getJSON(args[1], args[2], pollURL);
var serializer = new XmlSerializer(typeof(StatusDocumentItem));
StatusDocumentItem result;
using (TextReader reader = new StringReader(operationXML))
{
result = (StatusDocumentItem)serializer.Deserialize(reader);
}
Console.WriteLine(result.Message);
But my result object is always empty. What am I doing wrong?
Update. The value I get from my operationXML is like this and has an unnecessary xmlns attribute that is blocking my deserialization. Without that attribute, everything is working fine. Here is how it looks like:
"<StatusDocumentItem xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><DataUrl/><LastUpdated>2013-02-01T12:35:29.9517061Z</LastUpdated><Message>Job put in queue</Message><State>0</State><StateName>Waiting to be processed</StateName></StatusDocumentItem>"
Try this:
string xml = "<StatusDocumentItem xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\"><DataUrl/><LastUpdated>2013-02-01T12:35:29.9517061Z</LastUpdated><Message>Job put in queue</Message><State>0</State><StateName>Waiting to be processed</StateName></StatusDocumentItem>";
var serializer = new XmlSerializer(typeof(StatusDocumentItem));
StatusDocumentItem result;
using (TextReader reader = new StringReader(xml))
{
result = (StatusDocumentItem)serializer.Deserialize(reader);
}
Console.WriteLine(result.Message);
Console.ReadKey();
Does it show "Job put in queue"?
This generic extension works well for me....
public static class XmlHelper
{
public static T FromXml<T>(this string value)
{
using TextReader reader = new StringReader(value);
return (T) new XmlSerializer(typeof(T)).Deserialize(reader);
}
}