I am sending request to an api with HttpClient. The response from the request works without any problem, but I cannot parse the key values I want in the response. According to the research I have done, I tried such a code, but the incoming data returns empty in this way. How can I get the values I want from the incoming data?
using LoggerApi.Methods;
using System;
using System.Net.Http;
using System.Text;
using Newtonsoft.Json;
using System.Linq;
namespace LoggerApi.Methods
{
public class ApiMethods
{
public async static Task<object> GetClientInformations(string authenticationCode, string username = "username")
{
var client = new HttpClient();
var userInformationEndpoint = new Uri("https://myurl.com/url");
var userInformationPayload = new UserInformationPayload()
{
Login = username
};
var serializePayload = JsonConvert.SerializeObject(userInformationPayload);
var payload = new StringContent(serializePayload, Encoding.UTF8, "application/json");
var res = await client.PostAsync(userInformationEndpoint, payload).Result.Content.ReadAsStringAsync();
var responseResultJson = JsonConvert.DeserializeObject<object>(res);
return responseResultJson;
}
}
}
this code output is empty looks like this
{
"HasError": [],
"AlertType": [],
"AlertMessage": [],
"ModelErrors": [],
"Data": [
[
[]
],
[
[
[
[
[]
],
[
[]
],
[
[]
],
[
[]
]
]
]
]
]
}
But when I return var res directly instead of var responseResultJson from the function, the result is like this. What I want to do here is to access values such as Login, FirstName, LastName, Id from the incoming data. How can I do that?
{"HasError":false,"AlertType":"success","AlertMessage":"Operation has completed successfully","ModelErrors":[],"Data":{"Count":1,"Objects":[{"Id":291031530,"CurrencyId":"TRY","FirstName":"Scott","LastName":"Marshall","MiddleName":"William","Login":"scotty3"}]}}
There are multiple possible solutions. Once of them is the following.
using System.Net.Http.Json;
using System.Text.Json;
namespace Program
{
class Program
{
// We need this to create a new ToDo
record TodoDto(int UserId, string Title, string Body);
// That's what's stored in the backend service and returned by the API
record TodoModel(int Id, int UserId, string Title, string Body);
public static async Task Main(string[] args)
{
// HTTP Client
var httpClient = new HttpClient();
// required as Properties are PascalCase in C# but JSON properties here are camelCase (that's usually the case)
var serializerOptions = new JsonSerializerOptions {
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
// create new todo
var newTodo = new TodoDto(1, "My new todo", "I need to...");
// Add todo in the backend
var response = await httpClient.PostAsJsonAsync<TodoDto>("https://jsonplaceholder.typicode.com/posts", newTodo, serializerOptions);
if(response.IsSuccessStatusCode){
// if we have a succesful request we can deserialize the response
var newTodoModel = JsonSerializer.DeserializeAsync<TodoModel>(await response.Content.ReadAsStreamAsync(), serializerOptions);
Console.WriteLine(newTodoModel);
}
else
{
Console.WriteLine($"Request failed with status code {(int)response.StatusCode} ({response.StatusCode})");
}
}
}
Some remarks:
I am using JsonPlaceholder for some sample API endpoints to provide a working example here
Deserializing the API response to JSON could fail e.g. when the API returns something unexpected, so you should check for that especially if you don't control the API that you're calling.
For this to work you need to include the System.Net.Http.Json namespace at it contains the PostAsJsonAsync() extension method (see HttpClienJsonExtensions Class - Microsoft Learn for more nice and convenient extension methods).
There is no need for Newtonsoft JSON parser, as you can now use the built-in JSON Parser in the System.Text.Json namespace.
I prefer working with Streams as you can then use async all the way throughout your code which is a best practice (see Async best practices - Async All the Way)
By default a case-sensitive matching between your C# class and the JSON will be attempted. JSON usually uses camelCase and this API does so as well. Therefore we need to tell the (de-)serializer to use camelCase as well using JsonSerializerOptions.
Related
You'd think this would be easy as falling off a log, but I'm stumped. I have an ASP.NET 3.1 core web api (using System.Text.Json):
[HttpPost]
public async Task<ActionResult> CreateAsync(JsonElement dzc)
Which in turn calls a service:
public async Task AddItemAsync(JsonElement dzc)
{
var id = dzc.GetProperty("id").GetString(); // these are both valid
var userid = dzc.GetProperty("userid").GetString();
await this._container.CreateItemAsync<JsonElement>(dzc, new PartitionKey(userid));
}
This results in: Message: {"Errors":["The input content is invalid because the required properties - 'id; ' - are missing"]}
Or if I get the raw text:
public async Task AddItemAsync(JsonElement dzc)
{
var id = dzc.GetProperty("id").GetString(); // these are all valid
var userid = dzc.GetProperty("userid").GetString();
var raw = dzc.GetRawText();
await this._container.CreateItemAsync<string>(raw, new PartitionKey(userid));
}
and raw is:
{
"id": "foozy",
"userid": "foozy",
"name": "Jayb",
"fc": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"capacity": "10",
},
"geometry": {
"type": "Point",
"coordinates": [
-71.073283,
42.417500
]
}
}
]
}
}
This results in: Message: {"Errors":["One of the specified inputs is invalid"]}
All I'm really trying to do is shovel some Json directly from an API into CosmosDB without using C# classes. What is the magic incantation?
Short answer...you can't via CreateItemAsync. The documentation explicitly states that an id prop is required, which by definition does not exist on on either JsonElement or string.
A better option would be to use CreateItemStreamAsync which allows you to pass in the raw stream directly and appears to bypass an Id property requirement, per the example in the documentation.
using (Response response = await this.Container.CreateItemStreamAsync(partitionKey: new PartitionKey("streamPartitionKey"), streamPayload: stream))
{
using (Stream responseStream = await response.ContentStream)
{
//Read or do other operations with the stream
using (StreamReader streamReader = new StreamReader(responseStream))
{
string responseContentAsString = await streamReader.ReadToEndAsync();
}
}
}
Thanks David L, your suggestion worked great! Here's what I ended up with:
public async Task AddItemAsync(JsonElement dzc)
{
var id = dzc.GetProperty("id").GetString();
var userid = dzc.GetProperty("userid").GetString();
var raw = dzc.GetRawText();
byte[] bytes = Encoding.UTF8.GetBytes(raw);
using (var stream = new MemoryStream(bytes))
{
await this._container.CreateItemStreamAsync(stream, new PartitionKey(userid));
}
}
I'm curious if the copy can be avoided.
I'm having trouble getting just the price value from this API. I don't need all the JSON that's coming from the web URL, I only need the "rate":"3,394.2033" part.
API Data:
{
"time": {
"updated": "Feb 6, 2019 22:02:00 UTC",
"updatedISO": "2019-02-06T16:02:00-06:00",
"updateduk": "Feb 6, 2019 at 22:02 GMT"
},
"disclaimer": "This data was produced from the CoinDesk Bitcoin Price Index (USD). Non-USD currency data converted using hourly conversion rate from openexchangerates.org",
"bpi": {
"USD": {
"code": "USD",
"rate": "3,394.2033",
"description": "United States Dollar",
"rate_float": 3394.2033
},
"XBT": {
"code": "XBT",
"rate": "1.0000",
"description": "Bitcoin",
"rate_float": 1
}
}
}
My Code:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class API : MonoBehaviour
{
private string www =
"https://api.coindesk.com/v1/bpi/currentprice/XBT.json";
public Text responseText;
public void Request()
{
WWW request = new WWW(www);
StartCoroutine(OnResponse(request));
}
private IEnumerator OnResponse(WWW req)
{
yield return req;
responseText.text = req.text;
}
}
This script allows me to pull all the JSON data but I only need the "rate":"3,394.2033". More specifically, just the value 3,394.2033.
Using Json.Net's LINQ-to-JSON API (JTokens) you can do this with one line of code:
var rate = (string)JToken.Parse(json).SelectToken("bpi.USD.rate");
Fiddle: https://dotnetfiddle.net/Krgejr
You can use newtonsoft and follow this example here : https://www.newtonsoft.com/json/help/html/SerializingJSONFragments.htm
public class SearchResult
{
public string Rate { get; set; }
}
JObject rateSearch = JObject.Parse(MyJsonText);
// get JSON result objects into a list
IList<JToken> results = rateSearch ["bpi"]["USD"].Children().ToList();
// serialize JSON results into .NET objects
IList<SearchResult> searchResults = new List<SearchResult>();
foreach (JToken result in results)
{
// JToken.ToObject is a helper method that uses JsonSerializer internally
SearchResult searchResult = result.ToObject<SearchResult>();
searchResults.Add(searchResult);
}
You can also use the old but gold SimpleJSON from the Unity community
To use SimpleJSON in Unity you just have to copy the SimpleJSON.cs file into your projects "plugins" folder inside your assets folder.
and do e.g.
var json = JSON.Parse(req.text);
var yourText = json["bpi"]["USD"]["rate"];
Note for debugging
If the given key wasn't found in contrary to what you would expect this returns null instead of throwing an exception.
You can use UnityWebRequest.Get to get the JSON and then use a library to deserialize it (I'd recommend NewtonSoft.Json).
After deserializing the JSON you can choose which properties to use from the resulting object.
I put together a tutorial doing just that and a bit more.
https://www.youtube.com/watch?v=Yp8uPxEn6Vg
I'm trying to send an AJAX PATCH request to a Web API method and have the patched object recognised by Marvin.JsonPatch.
So far, everything I've sent to the server has resulted in an empty request being received.
The Web API controller method looks like this:
public async Task<IHttpActionResult> Update(long orderId, JsonPatchDocument<Order> patchedOrder)
I'm POSTing using an HttpClient like this (can't use async in this application)...
var patchDoc = new JsonPatchDocument<Order>();
patchDoc.Replace(e => e.Price, newPrice);
patchDoc.Replace(e => e.WordCount, newWordCount);
var request = new HttpRequestMessage(new HttpMethod("PATCH"), uri)
{
Content = new StringContent(JsonConvert.SerializeObject(patchDoc),
System.Text.Encoding.Unicode,
"application/json")
};
HttpResponseMessage response;
using (var client = new HttpClient(...))
{
response = client.SendAsync(request).GetAwaiter().GetResult();
}
But when the controller is it, the patchedOrder argument is null.
While debugging on the controller I've also tried
var s = await Request.Content.ReadAsStringAsync();
But this returns an empty string - can anyone explain why?
UPDATE:
This is what the contents of the JsonPatch document look like when passed in to the HttpClient...
{
"Operations": [{
"OperationType": 2,
"value": 138.7,
"path": "/price",
"op": "replace"
},
{
"OperationType": 2,
"value": 1320,
"path": "/wordcount",
"op": "replace"
}],
"ContractResolver": {
"DynamicCodeGeneration": true,
"DefaultMembersSearchFlags": 20,
"SerializeCompilerGeneratedMembers": false,
"IgnoreSerializableInterface": false,
"IgnoreSerializableAttribute": true,
"NamingStrategy": null
},
"CaseTransformType": 0
}
Somewhere during the development of Marvin.JsonPatch, the JsonPatchDocument<T> was annotated with an attribute that applied a custom JSON serializer:
[JsonConverter(typeof(JsonPatchDocumentConverter))]
This converter enables you to call JsonConvert.SerializeObject() on such a patch document and actually generate a patch document, as opposed to a representation of the JsonPatchDocument<T> CLR object.
Upgrade Marvin.JsonPatch and Newtonsoft.Json to the latest verison, and serialization should succeed.
I want to send a POST request in c# and i need the following sent through
"jsonrpc": "2.0",
"id": "12345",
"method": "my method",
"params": {
"api_key": "my api key",
"preset_id": "my preset id"
}
I tried using
using (WebClient client = new WebClient ())
{
byte [] response =
client.UploadValues ("my url", new NameValueCollection ()
{
{ "jsonrpc", "2.0" },
{ "id", "12345"},
{ "method", "my method"},
{ "params", ""}
});
string result = System.Text.Encoding.UTF8.GetString (response);
}
But i couldnt make the params an array, Please help, Thank you
It appears that you are asking for the parameters to be in an array, but they are actually shown as a "subclass". If the values were in an array, they should have square brackets around them.
However, both results are easy to achieve using anonymous (or real) classes (which I much prefer over embedding the property names in quoted text (makes future modifications much easier to implement).
var parameters = new
{
api_key = "my api key",
preset_id = "my preset id"
};
var json = new
{
jsonrpc = "2.0",
id = "12345",
method = "my method",
#params = parameters
};
string sResult = (new System.Web.Script.Serialization.JavaScriptSerializer()).Serialize(json);
The above code will result in the same output that you have shown. If you want an actual array instead, you can change the parameters definition to:
var parameters = new NameValueCollection();
parameters.Add("api_key", "my api key");
parameters.Add("preset_id", "my preset id");
Note that I used the .Net framework json serializer (from System.Web.Extensions), but you can use the serializer of your choice (we generally use NewtonSoft's JsonConvert).
I want to attach a work item on tfs to a build. i am reading SVN logs which contain the work item number and try to update the actual build to attach them.
workitems.Add(workItemStore.GetWorkItem(workitemid));
buildDetail.Information.AddAssociatedWorkItems(workitems.ToArray());
When I try to hit buildDetail.Information.Save(); or buildDetail.Save();, I get an AccessDeniedException.
See another post.
So I wanted to try with REST...
After loads of pages on MSDN, I concluded there is no .NET Client Library that takes care of builds. It looks like my only option is to patch a json into the TFS:
PATCH https://{instance}/DefaultCollection/{project}/_apis/build/builds/{buildId}?api-version={version}
How do I add the workitems the right way?
EDIT: I've found an old post which mentioned a dll in the TFS namespace which has the same capabilities like the call from above. Unluckily, it's not refered in MSDN. Same problem here: no way to add workitems.
To spread the issue and adress it to MS: Patrick has created a post on uservoice
UPDATE:
I managed to link a build in the work item. Here is an approach in c#:
var json = new JsonPatchDocument
{
new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/relations/-",
Value = new WorkItemRelation()
{
Rel = "ArtifactLink",
Url = build.Uri.ToString()
}
}
};
var client = new WebClient { UseDefaultCredentials = true };
client.Headers.Add(HttpRequestHeader.ContentType, "application/json-patch+json");
client.UploadData(
options.CollectionUri + "/_apis/wit/workitems/" + workitemid + "?api-version=1.0",
"PATCH",
Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(json)));
But there is still no direct binding in the build:
And the work items says unknown executable linktype
According to the message given in the workitem, I assume that I am using the false linktype. Does anybody have a reference for me, what types I am able to and should use?
URI UPDATE:
I am already using the mentioned uri:
MINOR SOLUTION:
I had to add the name "Build" to the Attributes of the patch. I still does not recognize it in the build itself but for now, I can work with the link as build type.
var json = new JsonPatchDocument
{
new JsonPatchOperation()
{
Operation = Operation.Add,
Path = "/relations/-",
Value = new WorkItemRelation()
{
Rel = "ArtifactLink",
Url = build.Uri.ToString()
Attributes = new Dictionary<string, object>()
{
{ "name", "Build" },
{ "comment", build.Result.ToString() }
}
}
}
};
You could add a workitem to a build by updating the workitem to add a relation link to the build via Rest API. Refer to this link for details: Add a Link.
After you add a link to the build in the workitem, the workitem would be show in the build summary.
Following is the content sample of the body,
[
{
"op": "test",
"path": "/rev",
"value": "2"
},
{
"op": "add",
"path": "/relations/-",
"value":
{
"rel": "ArtifactLink",
"url": "vstfs:///Build/Build/12351"
}
}
]
Add code sample:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
namespace AssociateWorkItemToBuild
{
class Program
{
static void Main(string[] args)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(
ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "username", "password"))));
string Url = "https://xxxxxx.visualstudio.com/_apis/wit/workitems/149?api-version=1.0";
StringBuilder body = new StringBuilder();
body.Append("[");
body.Append("{");
body.Append("\"op\":\"add\",");
body.Append(" \"path\":\"/relations/-\",");
body.Append("\"value\":");
body.Append("{");
body.Append("\"rel\": \"ArtifactLink\",");
body.Append("\"url\": \"vstfs:///Build/Build/12596\"");
body.Append("}");
body.Append("}");
body.Append("]");
var method = new HttpMethod("PATCH");
var request = new HttpRequestMessage(method, Url)
{
Content = new StringContent(body.ToString(), Encoding.UTF8,
"application/json-patch+json")
};
using (HttpResponseMessage response = client.SendAsync(request).Result)
{
response.EnsureSuccessStatusCode();
string responseBody = response.Content.ReadAsStringAsync().Result;
}
}
}
}
}
Update
Just as Eddie replied, you could add a workitem to a build by updating the workitem to add a relation link to the build via Rest API.
About LinkType there has been a clear demo in Eddie's answer. You need to use build uri.
The format needs to be vstfs:///Build/Build/8320
The BuildUri for the TFS Build tasks is a property that needs to be set so that those tasks can communicate with the server about the build they are performing actions for.
You can also use $env:BUILD_BUILDURI in a powershell script, more detail info and ways you can also refer this blog from MSDN: Build association with work Items in vNext Finally will get :