How can I deserialize the json response of azure devOps? - c#

I am reffering my question to the Microsoft doc. I requested the azure devOps api to retrieve all repos hosted in a particular organization and project. The response is a json looking like following:
{
"count": 3,
"value": [
{
"id": "5febef5a-833d-4e14-b9c0-14cb638f91e6",
"name": "AnotherRepository",
"url": "https://dev.azure.com/fabrikam/_apis/git/repositories/5febef5a-833d-4e14-b9c0-14cb638f91e6",
"project": {
"id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
"name": "Fabrikam-Fiber-Git",
"url": "https://dev.azure.com/fabrikam/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
"state": "wellFormed"
},
"remoteUrl": "https://dev.azure.com/fabrikam/Fabrikam-Fiber-Git/_git/AnotherRepository"
},
{
"id": "278d5cd2-584d-4b63-824a-2ba458937249",
"name": "Fabrikam-Fiber-Git",
"url": "https://dev.azure.com/fabrikam/_apis/git/repositories/278d5cd2-584d-4b63-824a-2ba458937249",
"project": {
"id": "6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
"name": "Fabrikam-Fiber-Git",
"url": "https://dev.azure.com/fabrikam/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c",
"state": "wellFormed"
},
"defaultBranch": "refs/heads/master",
"remoteUrl": "https://dev.azure.com/fabrikam/_git/Fabrikam-Fiber-Git"
},
{
"id": "66efb083-777a-4cac-a350-a24b046be6be",
"name": "TestGit",
"url": "https://dev.azure.com/fabrikam/_apis/git/repositories/66efb083-777a-4cac-a350-a24b046be6be",
"project": {
"id": "281f9a5b-af0d-49b4-a1df-fe6f5e5f84d0",
"name": "TestGit",
"url": "https://dev.azure.com/fabrikam/_apis/projects/281f9a5b-af0d-49b4-a1df-fe6f5e5f84d0",
"state": "wellFormed"
},
"defaultBranch": "refs/heads/master",
"remoteUrl": "https://dev.azure.com/fabrikam/_git/TestGit"
}
]
}
The Microsoft Doc says that the api returns type GitRepository[] so I want to deserialize the json into that type but how can I do that??
Because of the count and value property I built a HelperClass like following:
public class Rootobject
{
public GitRepository[] value { get; set; }
public int count { get; set; }
}
But after the deserialization and cast into that object all the props of GitRepository are null.
Can someone help me why all the properties are null??
EDIT: This is the deserialization
static async Task Main(string[] args)
{
string accessToken = "personal_access_token";
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes(string.Format("{0}:{1}", string.Empty, accessToken))));
var response = await httpClient.GetStringAsync("https://dev.azure.com/fabrikam/_apis/git/repositories?api-version=5.1");
var responseObject = JsonSerializer.Deserialize<Rootobject>(response);
Console.WriteLine();
}

How are you doing your deserialization? Where have you defined the GitRepository class? Have you tried deserializing into string to check if any members have changed? The documentation is from 2016, and might have changed since.
You could instead use the nuget packages provided by Microsoft to access these APIs: https://learn.microsoft.com/en-us/azure/devops/integrate/concepts/dotnet-client-libraries?view=azure-devops
I think the NuGet package you need is Microsoft.TeamFoundationServer.Client

The problem is that System.Text.Json.JsonSerializer is by default case-sensitive. GitRepository property names are PascalCase while your provided json example contains CamelCase names. So JsonSerializer can't deserialize those properties correctly.
You can override the default settings by providing JsonSerializerOptions:
var responseObject = JsonSerializer.Deserialize<Rootobject>(response, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
Also I noticed that project property is still not being deserialized. I guess it's because GitRepository has a property called ProjectReference (instead of Project) and has a DataMember attribute.
It looks like System.Text.Json doesn't respect the DataMember attribute. So I suggest you use Newtonsoft.Json library. I tested it and it works fine:
var responseObject = Newtonsoft.Json.JsonConvert.DeserializeObject<Rootobject>(response);

Related

Deserialize json to a customize object with json.net

I'm trying to deserialize json-formatted response below.
{
"context": "xxxxxx"
"value": [
{
"Id": "123"
"Time": "2022-12-01"
}
{
"Id": "123"
"Time": "2022-12-01"
}
....
]
}
According to this: https://www.newtonsoft.com/json/help/html/deserializeobject.htm, this code should work.
public class WorkingSetContent
{
/// <summary>Collection ID</summary>
[JsonProperty("context")]
public string Context { get; set; }
/// <summary>UserRelationship</summary>
[JsonProperty("value")]
public IList<ItemClass> Items { get; set; }
}
But I'm getting a build error : "Change 'Items' to be read-only by removing the property setter."
I changed the setter to private to avoid this build error, then I was able to run it, but it causes a runtime error as null value is passed.
This is the code analysis rule you're running up against.
One option is to initialize your collection property with an empty list:
[JsonProperty("value")]
public IList<ItemClass> Items { get; } = new List<ItemClass>();
However, the code analysis rule explicitly says:
You can suppress the warning if the property is part of a Data Transfer Object (DTO) class.
Since you're deserializing this class from JSON, I think it's safe to assume it's a DTO. I would recommend using a pattern in your .globalconfig or .editorconfig files to suppress this rule for all of your DTO model classes.
You have to fix your json
{
"context": "xxxxxx",
"value": [
{
"Id": "123",
"Time": "2022-12-01"
},
{
"Id": "123",
"Time": "2022-12-01"
}
]
}

Strongly Typed IDs and JSON Schema generation in C#

I like the idea of Strongly Typed IDs and I am using it for some time. I use it also in DTOs without and major problems... till now. I have a use case where I need to generate JSON Schema from some classes which uses Strongly Type IDs like following:
[StronglyTypedId(StronglyTypedIdBackingType.Long)]
public partial struct TweetId
{
}
public class Tweet
{
public TweetId Id {get; set;}
}
So, as described, TweetID is represented as long base type.
The problem arises when I want to generate JSON Schema from this type. I am currently using Newtonsoft.Json.Schema (I also tried some others) using this code:
JSchemaGenerator generator = new JSchemaGenerator();
JSchema quotesSchema = generator.Generate(typeof(Tweet));
The result I get is:
{
"definitions": {
"Tweet": {
"type": [
"object"
],
"properties": {
"Id": {
"$ref": "#/definitions/TweetId"
}
}
},
"TweetId": {},
},
"type": "object",
"$ref": "#/definitions/Tweet"
}
But what I want is:
{
"definitions": {
"Tweet": {
"type": [
"object"
],
"properties": {
"Id": {
"type": "integer"
}
}
}
},
"type": "object",
"$ref": "#/definitions/Tweet"
}
Do you have any idea how to achive this? Newtonsoft.Json.Schema is not mendatory.

Convert Swagger to C# object [duplicate]

This question already has answers here:
How do deserialize JSON with non-standard (and varying) property names (in .NET)
(3 answers)
Closed 5 years ago.
Is there an easy way to convert Json string to C# object?
I have a swagger file in json format and need to make it to C# object
{
"swagger": "2.0",
"info": {
"version": "Profiles",
"title": "Profiles"
},
"paths": {
"/api/Profiles/Get": {
"get": {
"tags": [ "Profiles"],
"operationId": "Profiles_GetById",
"consumes": [],
"produces": [],
"parameters": [{ "name": "id"}]
}
},
"/api/Profiles": {
"get": {
"tags": [
"Profiles"
],
"operationId": "Profiles_GetBySubscriptionid",
"consumes": [],
"produces": [],
"parameters": [{ "name": "subscriptionId"}]
}
}
},
"definitions": {}
}
So the problem that I am facing right now is that I have no idea how to convert paths to properties of my C# object. Specifically, I have no clue how I define a C# object properties for "/api/Profiles/Get", or "/api/Profiles".
public class SwaggerObject
{
[JsonProperty("paths")]
public SwaggerPath Paths { get; set;}
}
public class SwaggerPath {
...
}
If you have a valid swagger definition, you can use AutoRest to generate a client for you. The auto-generated client includes models for all of the types you define in your swagger document.
You can then call Newtonsoft.Json.JsonConvert.DeserializeObject<MyModel>(json​) after you retrieve the response. Better yet, you can you the auto generated client to make the HTTP calls in your .NET code.

In C#, how can i deserialize this json?

I am getting this JSON data back from a REST service that looks like this:
[ [ [ {"Name": "Joe", "Comment": "Load", "Updated": null, "Test": ""} ] ] ]
or
[ [ [ {"Name": "Joe", "Comment": "Load", "Updated": null, "Test": ""}, {"Name": "Bill", "Comment": "123", "Updated": null, "Test": ""} ] ] ]
I did a "Copy JSON as classes" feature in visual studio and it created this:
public class Rootobject
{
public Project[][][] Property1 { get; set; }
}
public class Project
{
public string Name { get; set; }
public string Comment { get; set; }
public string Updated { get; set; }
public string Test { get; set; }
}
but when i try to deserialize using this code:
var results = new JsonDeserializer().Deserialize<Rootobject>(response);
I get an error stating:
An exception of type 'System.InvalidCastException' occurred in RestSharp.dll but was not handled in user code
Additional information: Unable to cast object of type 'RestSharp.JsonArray' to type 'System.Collections.Generic.IDictionary`2[System.String,System.Object]'
Could you please advise on what I might be doing wrong (NOTE: i don't have any control over how the data comes in so changing the format isn't an option)
Also, to confirm this seems to be valid JSON from JSONLlint:
Using Newtonsoft.Json nuget package,
try
JSONConvert.DeserialiseObject<Rootobject>(response)
EDIT: BTW, I tried to use your json on http://json2csharp.com/ and it says Parsing your JSON didn't work. Please make sure it's valid. So I doubt that any json parsing library will be able to parse your JSON.
However implementing your own deserializer is possible and an ideal solution when external services return invalid JSON
I can probably help you deserialize it if you show me what JSON you get when service returns multiple Project objects.
EDIT2: Szabolcs's solutions seems promising, but I would still suggest testing it with JSON for multiple Product objects. I smell something bad & its the shitty third party service. Always good to test.
You can deserialize that particular JSON like this using Json.NET:
var json = "[ [ [ {\"Name\": \"Joe\", \"Comment\": \"Load\", \"Updated\": null, \"Test\": \"\"}, "+
" {\"Name\": \"Bill\", \"Comment\": \"123\", \"Updated\": null, \"Test\": \"\"} ] ] ]";
var deserializedObject = JsonConvert.DeserializeObject<List<List<List<Project>>>>(json);
And to get all the Projects from the nested lists:
var allProjects = deserializedObject.SelectMany(x => x.SelectMany(y => y)).ToList();

retrieve item from a json file c#

{
"_id": "underscore",
"_rev": "136-824a0ef7436f808755f0712c3acc825f",
"name": "underscore",
"description": "JavaScript's functional programming helper library.",
"dist-tags": {},
"versions": {
"1.0.3": {
"name": "xxx",
"description": "xxx"
},
"1.0.4": {},
"1.1.0": {}
}
}
I would like to retrieve the latest version(1.1.0) from the json file. However, it always gives out me errors of "can not deserialize json object into type RootObject
Here is my class
public class versions
{
public string name { get; set; }
public string description { get; set; }
}
public class RootObject
{
public List<versions> vs { get; set; }
}
And here is where I used it
RootObject[] dataset = JsonConvert.DeserializeObject<RootObject[]>(json);
Any idea. Many thankx
Update:
I have updated the JSON file format, but some problem..
I think the problem is, that in JSON you have to quote all "field"/attribute names. (Thats a difference from standard Javascript-Notation, where you can have unquoted attributes).
So, your file should be like:
{
"_id" : "underscore",
"versions": {
"1.0.3" : {
"name": "xxx",
"description": "xxx"
}
}
Note that {1.0.3: { name: "xxx" } } wouldn't be valid JavaScript either since '1.0.3' is an invalid identifier in JavaScript.
Looking at the JSON in your updated answer:
{
"_id" : "underscore",
"versions": {
"1.0.3" : {
"name": "xxx",
"description": "xxx"
},
"1.0.4" : {
"name": "xxx",
"description": "xxx"
}
}
This is still Invalid JSON - you have 4 opening { and only 3 closing }
you should use http://jsonlint.com/ - to validate your JSON and ensure it is Valid
I've fixed your json in question. Now for your real question
I would like to retrieve the latest version(1.1.0) from the json file. However, it always gives out me errors of "can not deserialize json object into type RootObject
You have property names like 1.0.3 that are unknown at compile time. So you can not deserialize them to a concrete class. You should handle them dynamically.
Try this:
var versions = JObject.Parse(json)["versions"]
.Children()
.Cast<JProperty>()
.ToDictionary(c => c.Name, c => c.Value.ToObject<versions>());

Categories