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
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.
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've been searching the internet for literally hours trying to find a very simple example of serialization and deserialization with a JSON call in C#. After careful browsing and piecing together, I'd like to call a JSON (POST & GET) function in my webservice (see below).
Here's what I was able to piece together
This would be my service contract (IExecWebservice.svc)
using System.ServiceModel;
using System.ServiceModel.Web;
namespace _27963199
{
[ServiceContract]
public interface IExecFunction
{
[WebGet(UriTemplate = "/{function}/{args}")]
double CalcThis(string function, string args);
}
}
In my main code I parse out the users request URI (IExecFunction.cs)
//user will send variables in REST URI http://myCalcServer/CalcThis/MethodA/10,20
using FunctionLibrary;
using System;
using System.Reflection;
namespace _27963199
{
public class ExecFunctionService : IExecFunction
{
public double CalcThis(string function, string args)
{
Type t = typeof(Functions);
MethodInfo[] libraryFunctions = t.GetMethods(BindingFlags.Static | BindingFlags.Public);
string[] arguments = args.Split(',');
//Missing piece of code where I split the URI for the JSON function that will POST the data object to be be calculated by the DROOLS/Rules Engine and the results passed back to the users web browser
...
}
}
}
Now in my separate function class I'd have something like this (Function.cs)
using System;
using newton.json;
using System.Net.Http;
namespace FunctionLibrary
{
public static class Functions
{
public static double DoMathA(string url, string arg1, string arg2)
{
double d1;
double d2;
if (!double.TryParse(arg1, out d1) || !double.TryParse(arg2, out d2))
{
throw new ArgumentException("Arguments to function 'DoMathA' must be numeric.");
}
//Data Object Format "{'myData':{'Id':'5','var1':'10','var2':'90'}}"
myCalcObject = "{'myData':{'Id':'5', & arg1 & :'10', & arg2 & :'90'}}"
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json; charset=utf-8";
DataContractJsonSerializer ser = new DataContractJsonSerializer(data.GetType());
MemoryStream ms = new MemoryStream();
ser.WriteObject (myCalcObject)
String json = Encoding.UTF8.GetString(ms.ToArray());
StreamWriter writer = new StreamWriter(request.GetRequestStream());
writer.Write(json);
writer.Close();
}
}
...
//Missing piece of code where I want to return the results of the JSON PUT and GET to the calling webservice
//JSON output string looks like this {"YourResults":{"condition":"YourHairIsOnFire","alertlevel":100,"id":0}}
//return or parse (json) to XLMS on the users browser
}
I need help filling in the blanks so that the request URI is parsed properly to be passed into the JSON function and the reply JSON string be translated back as an xlms on the users browser. Any thoughts?
Update: I tried getting just the JSON section to work as a standalone C# class but I get that "Expected class..." error when I compile it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Net.Http;
using System.Net;
using Newtonsoft.Json;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(http://172.16.20.26:8080/myDrools/result);
request.Method = "POST";
request.ContentType = "application/json; charset=utf-8";
DataContractJsonSerializer ser = new DataContractJsonSerializer(data.GetType());
MemoryStream ms = new MemoryStream();
ser.WriteObject ("{'myData':{'Id':'5','var1':'4.5','var2':'8.7'}}")
String json = Encoding.UTF8.GetString(ms.ToArray());
StreamWriter writer = new StreamWriter(request.GetRequestStream());
writer.Write(json);
writer.Close();
What Am I doing wrong here? How can I get an XMLS output from this?
You have several bits and pieces. If you glue them together it can work.
Service Interface
Make sure your interface is capable of returning an object that holds the data you expect to return. By making fields nullable you can have variations in how the result looks like.
Notice the ResponseFormat=WebMessageFormat.Json to have json as the returned content type.
[ServiceContract]
public interface IExecFunction
{
[WebGet(UriTemplate = "/{function}/{args}", ResponseFormat=WebMessageFormat.Json)]
[OperationContract]
Result CalcThis(string function, string args);
}
// the result class that actsd as an container
public class Result
{
public Double DoubleResult { get; set; }
public Int32 IntResult { get; set; }
public string Message { get; set; }
}
Service Implementation
The service implementation is bit more involved because it first needs to parse the arguments and convert them to proper types. When that is done it can use reflection to find an appropriate method to call. The returned type is then converted/projected on the Result.
public class ExecFunctionService : IExecFunction
{
// this is a GET /fubar/1,2,3,4
public Result CalcThis(string function, string args)
{
// function=fubar
// args = 1,2,3,4
var allargs = args.Split(',');
// store each argument with their type
var typeList = new List<Tuple<Type, object>>();
// parsr to gind the best match
foreach(var arg in allargs)
{
// convert each argument string
// to a type that is supported
int i;
if (Int32.TryParse(arg, out i))
{
typeList.Add(new Tuple<Type,object>(typeof(Int32), i));
continue;
}
double d;
if (Double.TryParse(arg,
NumberStyles.AllowDecimalPoint,
new CultureInfo("en-us"),
out d))
{
typeList.Add(new Tuple<Type,object>(typeof(Double), d));
continue;
}
// if all fails assume string
typeList.Add(new Tuple<Type,object>(typeof(string), arg));
}
// find and call the correct method
// notice that parameters and their type do matter
// overloads of the same methodname with
// different types is supported
// Functions is the static type with methods to call
var method = typeof(Functions).GetMethod(
function,
BindingFlags.Static| BindingFlags.Public |BindingFlags.InvokeMethod,
null,
typeList.Select(ty => ty.Item1).ToArray(), //all types
null);
var callresult = method.Invoke(
null,
typeList.Select(ty => ty.Item2).ToArray()); // all values
// shape the output in the form you need
var result = new Result();
if(callresult is double)
{
result.DoubleResult = (double) callresult;
}
if (callresult is int)
{
result.IntResult = (int)callresult;
}
if (callresult is string)
{
result.Message = (string)callresult;
}
return result;
}
}
Your Functions to be called
This is the class that holds all your methods that can be called from the service.
// your calc functions go here
public static class Functions
{
public static double DoMathA(double arg1, double arg2)
{
return arg1 / arg2;
}
public static double DoMathB(int number, double factor)
{
return number * factor;
}
public static int DoMathC(string somestring)
{
return somestring.GetHashCode();
}
}
What does it look like?
Calling http://localhost/service1.svc/DoMathC/fubar will return:
{"DoubleResult":0,"IntResult":418978654,"Message":null}
and http://localhost/service1.svc/DoMathA/2.5,3.4 will return:
{"DoubleResult":0.73529411764705888,"IntResult":0,"Message":null}
and http://localhost/service1.svc/DoMathB/4,3.5 will return:
{"DoubleResult":14,"IntResult":0,"Message":null}
Think of it this way: instead of trying to glue together a string, return an object. The service will take care of stringifying it for you.
So, create a class with properties to match the JSON object, and then set your values into those properties, then return the object. Done.
Don't overthink it
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 :(