I'm trying to collect data from a client through an API. Eventually sending individual parts of the data that I collected to another project.
So for example I'm collecting an entire data object with id,Name,Status, etc. And I only want the id variable with it's value to be send to another project.
Everything works fine except from the sending part. I still had to paste all related classes to make it easier to understand.
I'm trying to send a list of objects. The problem is that I don't know how to send it the right way. You can see what I tried below. I'm creating my own object named WatchdogDTO which contains only the values that I want to send. Eventually I serialize it to send it to http://localhost:53661/api/Values. Which is another project I use to test if it works. Now it just literally sends the name of the list instead of the actual WatchdogDTO.
This is the data I'm collecting:
[
{
"id": "200",
"name": "Kerno (Camera)",
"hostName": "Amsterdam",
"status": false,
"hasCamera": true,
"cameraStatus": "0",
"lastSeen": "34324"
},
{
"id": "202",
"name": "Bassy (Location)",
"hostName": "Limburg",
"status": true,
"hasCamera": false,
"cameraStatus": "-1",
"lastSeen": "2344"
}
]
I created a corresponding model:
public class ServerStateDto
{
public string id { get; set; }
public string Name { get; set; }
public string HostName { get; set; }
public bool Status { get; set; }
public bool hasCamera { get; set; }
public string CameraStatus { get; set; }
public DateTime LastSeen { get; set; }
}
In the class ApiManager I collect the data above and deserialize it:
class ApiManager
{
HttpClient client = new HttpClient();
public List<ServerStateDto> CollectWatchdogData()
{
client.BaseAddress = new Uri("https://classified.Data.net:4802");
HttpResponseMessage response = client.GetAsync("/api/pdf/process/getalldata").Result;
if (response.IsSuccessStatusCode)
{
string dto = response.Content.ReadAsStringAsync().Result;
List<ServerStateDto> model = Newtonsoft.Json.JsonConvert.DeserializeObject<List<ServerStateDto>>(dto);
return model;
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return null;
}
}
}
This is just a general class that calls the functions CollectWatchdogData(); and PostWatchdogData();
{
public bool Start(HostControl hostControl)
{
ApiManager x = new ApiManager();
List<ServerStateDto> returnedData = x.CollectWatchdogData();
if (returnedData == null)
{
return false;
}
WatchdogApiClient cli = new WatchdogApiClient();
cli.PostWatchdogData(returnedData);
return true;
}
}
This is a model for the object that I'm sending. I only want to send Id from the model above:
public class WatchdogDTO
{
public string id { get; set; }
}
This class actually sends the WatchdogDTO from above to localhost:53661 which is another visual studio project of my which I use to test if the sending works.
public class WatchdogApiClient
{
private static readonly HttpClient client = new HttpClient();
//public async void PostWatchdogData(ServerStateDto toSend)
public async void PostWatchdogData(List<ServerStateDto> toSend)
{
//to actually send the data
WatchdogDTO toActuallySend = new WatchdogDTO()
{
id = toSend.ToString()
};
//Serialize data to bytestream so it can be sent
string serializedWatchDogData = JsonConvert.SerializeObject(toActuallySend);
if (string.IsNullOrEmpty(serializedWatchDogData))
{
return;
try
{
var response = await client.PostAsync("http://localhost:53661/api/Values", new StringContent(serializedWatchDogData, Encoding.UTF8, "application/json"));
}
catch (Exception ex)
{
Console.WriteLine($"Encountered an exception during PostAsync in method PostWatchDogData: {ex.Message}");
throw ex;
}
}
}
Eventually the destination of the data has a class and a model aswell to receive the data sent
model:
public class WatchdogDTO
{
public string id { get; set; }
}
This is the sending part, the sending works but the problem is that the value of WatchdogDTO data is the name of the list instead of the actual list with its values.
Class to receive the data send:
public class ValuesController : ApiController
{
[HttpPost]
public void PostWatchdogData([FromBody] WatchdogDTO data)
{
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.BroadcastMessage("Server", "b");
}
}
How can I send individual parts like for example only the id, or the host name, or the id AND the hostname.
Thanks in advance and sorry for the bunch of code.
Related
I followed a tutorial (https://www.youtube.com/watch?v=aWePkE2ReGw) to try and call APIs using C#, and while it worked, my attempts to modify it for my own projects failed miserably. Despite my best efforts and trying various different links on the same website, they all return failure.
I was able to access it with python no problem, so the problem seems confined to C#. I have the microsoft.aspnet.webapi.client installed, as well as json. It compiles and runs, just returns failure.
https://dbd.tricky.lol/apidocs/
Here's the code:
namespace API_Test
{
// class used to initialize the httpclient
public class ApiHelper
{
public static HttpClient? ApiClient { get; set; }
public static void InitializeClient()
{
ApiClient = new HttpClient();
ApiClient.DefaultRequestHeaders.Accept.Clear();
ApiClient.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
}
// used to hold the result
class ShrineModel
{
public double ID { get; set; }
}
// used for processing results
class ShrineResultModel
{
public ShrineModel? Results { get; set; }
}
// used to access the api
class ShrineProcessor
{
public static async Task<ShrineModel> LoadShrineInformation()
{
string url = "https://dbd.tricky.lol/api/shrine";
using (HttpResponseMessage response = await ApiHelper.ApiClient.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
ShrineResultModel result = await response.Content.ReadAsAsync<ShrineResultModel>();
return result.Results;
}
else
{
// ALWAYS ENDS UP HERE
Console.WriteLine("Failure");
Console.ReadKey();
throw new Exception(response.ReasonPhrase);
}
}
}
}
// main
public class API_Test
{
static async Task Main()
{
ApiHelper.InitializeClient();
var ShrineInfo = await ShrineProcessor.LoadShrineInformation();
Console.WriteLine(ShrineInfo.ID);
}
}
}
Best case scenario I get a json output with all the information found at the link. What I'm getting is a false response from "IsSuccessStatusCode"
I've tried various other endpoints available from the same website. Other APIs have worked. But I can't get this one to.
I ran your code against the API and there are a couple of issues to fix.
1. 403 Forbidden
You got response.IsSuccessStatusCode false because the API endpoint requires a User-Agent header. This is to block any web crawler bots.
To fix that you just need to add this code,
ApiClient.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0");
2. Wrong JSON data model
You must model the JSON response structure so the deserialization can work properly.
The JSON response:
{
"id": 666,
"perks": [
{
"id": "Aftercare",
"bloodpoints": 100000,
"shards": 2000
},
{
"id": "BeastOfPrey",
"bloodpoints": 100000,
"shards": 2000
},
{
"id": "DeadHard",
"bloodpoints": 100000,
"shards": 2000
},
{
"id": "Nemesis",
"bloodpoints": 100000,
"shards": 2000
}
],
"start": 1672185600,
"end": 1672790399
}
So your ShrineResultModel should be like this:
public class Perk
{
public string id { get; set; }
public int bloodpoints { get; set; }
public int shards { get; set; }
}
public class ShrineResultModel
{
public int id { get; set; }
public List<Perk> perks { get; set; }
public int start { get; set; }
public int end { get; set; }
}
Update your code to deserialize the JSON response:
if (response.IsSuccessStatusCode)
{
ShrineResultModel result = await response.Content.ReadFromJsonAsync<ShrineResultModel>();
return result;
}
The actual result:
I am new to c# and I am willing to convert a Object into a class representing a User. I am retrieving the object from a http request to an API. My code is:
private async void getUser()
{
var requestURI = new HttpRequestMessage();
string url = "https:...";
requestURI.RequestUri = new Uri(url);
requestURI.Method = HttpMethod.Get;
requestURI.Headers.Add("Accept", "application/json");
HttpResponseMessage responseURI = await client.SendAsync(requestURI);
if (responseURI.StatusCode == HttpStatusCode.OK)
{
Debug.WriteLine("Get User OK");
var UserString = await responseURI.Content.ReadAsStringAsync();
Debug.WriteLine("User: " + UserString);
var UsersJson = JsonConvert.DeserializeObject(UsersString);
Debug.WriteLine(UserJson);
}
else
{
Debug.WriteLine("User GET request failed");
}
}
The output is:
{
"Username": "Alice",
"IP": "192.13.2.2",
"Levels": "1,2"
}
How do I create a class or a type to later deserealize this object into it? When the type/class is created, how do I deserealize the object into it?
Thanks in advice
You can use this website - https://json2csharp.com/ - to generate the C# classes.
In this case, the class would be as follows:
public class User
{
public string Username { get; set; }
public string IP { get; set; }
public string Levels { get; set; }
}
For deserialization to this class, you can use as follows:
var user = JsonConvert.DeserializeObject<User>(UsersString);
public class User
{
public string Username { get; set; }
public string IP { get; set; }
public string Levels { get; set; }
}
The you Deserialize an Object like this: Please have a look at this https://www.newtonsoft.com/json/help/html/SerializingJSON.htm
var user = JsonConvert.DeserializeObject<User>(json);
I'm writing an ASP.NET Core API, which should follow an existing format.
The response should always start with a data element in the json
{
"data": {
"batch_id": null,
"created_at": "2019-01-29T16:58:04+00:00",
"current_operation": null,
"description": "This watch has a new name",
"model": {
"brand_id": null,
"created_at": null,
...
}
}
}
I created a class Batch which contains all the properties that we see, and my controller is taking care of serialize it in the background.
public class Batch
{
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
[HttpGet("{id}")]
public ActionResult<Batch> Get(string id)
{
...
return batch;
}
Is there a way to write something so that every payload has to start with a data property?
I would like to avoid writing a class DataWrapper<T> that I have to use for every response.
You could just use an anonymous object to wrap the response from the controller:
[HttpGet("{id}")]
public ActionResult<Batch> Get(string id)
{
...
return Ok(new { data = batch });
}
You can use GenericClass
public class GenericResponseClass<T>{
public T data;
}
then you can pass your batch(or any other) class to GenericResposneClass just like
public class Batch
{
[JsonProperty("label")]
public string Label { get; set; }
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
}
GenericResponseClass<Batch> response = new GenericResponseClass<Batch>();
Batch batch = new Batch();
batch.Label = "New Label";
batch.SerialNumber = "Serial Number";
response.data = batch;
then pass/serialize your response
i have a solution where i consume a WebService using RestSharp providing JSON data. I have a class model like this, matching the received data:
public class PBase
{
public string Name { get; set; }
public virtual string GetValue()
{
return string.Empty;
}
}
public class PDouble : PBase
{
public double DoubleValue { get; set; }
public string Scale { get; set; }
public override string GetValue()
{
return DoubleValue.ToString(CultureInfo.InvariantCulture);
}
}
public class PBool : PBase
{
public bool? BoolValue { get; set; }
public override string GetValue()
{
if (!BoolValue.HasValue)
return null;
return BoolValue.Value ? "1" : "0";
}
}
Meerely deep within the data, such blocks as the following appear multiple times, holding the parameters of a parent object, which is not of much interest here (as everything else except the parameter thingy works fine):
"Parameters":
[
{
"__type": "PDouble:http://name.space.de/Parameter",
"Name": "DEPTH",
"DoubleValue": 5,
"Scale": "MM"
},
{
"__type": "PDouble:http://name.space.de/Parameter",
"Name": "HEIGHT",
"DoubleValue": 10,
"Scale": "MM"
},
{
"__type": "PBool:http://name.space.de/Parameter",
"Name": "ESSENTIAL",
"BoolValue": true
},
]
Now, the problem is, the deserialized data contains only PBase instances. So everything except Name is gone. How can i achieve correct deserialization?
EDIT:
For deserialization i have nothing special implemented, just like this:
var client = new RestClient()
{
BaseUrl = new Uri("MyURL")
};
var request = new RestRequest()
{
Resource = "MyResource",
Method = Method.GET
};
request.AddHeader("Accept", "application/json");
request.RequestFormat = DataFormat.Json;
var response = client.Execute<MyType>(request);
return response.Data;
Here is a sample of the JSON data I am getting back from a web service.
It has a single node of "odata.metadata", another single node of "value", and then the repeating records consisting of 3 fields "Code", "Name" and "XTag". This is in a string variable called Content
{"odata.metadata":"https://notthereal.domain:3148/system/OData/$metadata#pageLocationList","value":[{"Code":"LOC-A","Name":"Location A","XTag":"36;DgAAAAJ7/zEAMAAwAC0ATQBBAEkATgAAAAAA8;264943250;"},{"Code":"LOC-B","Name":"Location B","XTag":"36;DgAAAAJ7/zEAMAAxAC0ATQBBAEkATgAAAAAA8;388906690;"},{"Code":"LOC-C","Name":"Location C","XTag":"36;DgAAAAJ7/zEAMAAyAC0ATQBBAEkATgAAAAAA8;388844480;"},{"Code":"LOC-D","Name":"Location D","XTag":"36;DgAAAAJ7/zEAMAAzAC0ATQBBAEkATgAAAAAA8;388876720;"}]}
I have the following as the model, which I got by pasting the JSON data into the class.
public class ModelPageLocationList
{
public string odatametadata { get; set; }
public List<Value> value { get; set; }
}
public class Value
{
public string Code { get; set; }
public string Name { get; set; }
public string XTag { get; set; }
}
and this is the code trying to deserialise it...
DataPageLocationList =
JsonConvert.DeserializeObject<List<ModelPageLocationList>>(content);
..and this is the error message. I am stuck! Any ideas? I think the issue is around the 2 single records at the start, and then the repeating data after.
ERROR Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
Here is what I am calling
public async Task<List<ModelPageLocationList>> RefreshPageLocationListAsync ()
{
DataPageLocationList = new List<ModelPageLocationList>();
var uri = new Uri(string.Format(WebServiceSettings.WebServiceUrl, string.Empty));
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
Debug.WriteLine("{0}", content.ToString());
DataPageLocationList = JsonConvert.DeserializeObject<List<ModelPageLocationList>>(content);
}
} catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return (DataPageLocationList);
}
Two things. Use JsonProperty to handle the odata.metadata property as the dot (.) in the property name will cause syntax problems.
public class ModelPageLocationList {
[JsonProperty("odata.metadata")]
public string odatametadata { get; set; }
[JsonProperty("value")]
public List<Value> value { get; set; }
}
Next based on the provided JSON example data you need to refactor the calling code
public async Task<ModelPageLocationList> RefreshPageLocationListAsync () {
try {
var uri = new Uri(string.Format(WebServiceSettings.WebServiceUrl, string.Empty));
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode) {
var result = await response.Content.ReadAsAsync<ModelPageLocationList>();
return result;
}
} catch (Exception ex) {
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return null;
}
You need to serialize to your root object ModelPageLocationList, NOT a list of your root objects. Your JSON is an object not an array of objects. This will fix it:
DataPageLocationList =
JsonConvert.DeserializeObject<ModelPageLocationList>(content);
Without seeing the JSON Data you are trying to pass in, I can only provide two possible answers based on the information you have provided.
1) Make sure your JSON Looks like this:
{
"odatametadata": "SomeDataHere",
"value": [{
"code": "Code1",
"name": "Name1",
"xTag": "XTag1"
}, {
"code": "Code1",
"name": "Name1",
"xTag": "XTag1"
}, {
"code": "Code1",
"name": "Name1",
"xTag": "XTag1"
},
]
}
2 Add this attribute to your C# Class Model
public class ModelPageLocationList
{
[JsonProperty("odatametadata")]
public string odatametadata { get; set; }
[JsonProperty("value")]
public List<Value> value { get; set; }
}
public class Value
{
[JsonProperty("code")]
public string Code { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("xTag")]
public string XTag { get; set; }
}
I did the following to get my code to work with the JSON data at the start of my post. Not ideal, and I would prefer to be able to do this without having to edit the JSON string.
public async Task<List<ModelPageLocationList>> RefreshPageLocationListAsync ()
{
DataPageLocationList = new List<ModelPageLocationList>();
var uri = new Uri(string.Format(WebServiceSettings.WebServiceUrl, string.Empty));
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
Debug.WriteLine("BEFORE {0}", content.ToString());
content = content.Substring(content.IndexOf('['), content.IndexOf(']') - content.IndexOf('[') + 1);
Debug.WriteLine("AFTER {0}", content.ToString());
DataPageLocationList = JsonConvert.DeserializeObject<List<ModelPageLocationList>>(content);
}
}
catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return (DataPageLocationList);
}
and the class from the JSON data
public class ModelPageLocationList
{
public string Code { get; set; }
public string Name { get; set; }
public string ETag { get; set; }
}
Thanks everyone for their replies.