System.Text.Json Deserialize and get Property Values [closed] - system.text.json

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
I'm struggling to understand how to access the property values from a class which was derived by using the Visual Studio 'Paste Special' into a new C# Model. The JSON was copied from an API returning log events, in the example I've only included one log event but the API will usually return multiple events..
Below is JSON from the API:
{
"lastReadEventId": "event-1a29db8ad2c608d8079f040000000000",
"scannedEventCount": 1,
"eventEntities": [
{
"timestamp": "2021-02-16T21:59:15.7233546+00:00",
"properties": [
{
"name": "LogEventCategory",
"value": "System"
},
{
"name": "LogEventType",
"value": "Application Startup"
},
{
"name": "LogEventSource",
"value": "WebApp_RAZOR"
},
{
"name": "LogUserId",
"value": ""
},
{
"name": "LogUsername",
"value": ""
},
{
"name": "LogForename",
"value": ""
},
{
"name": "LogSurname",
"value": ""
},
{
"name": "LogData",
"value": "Application Starting Up"
},
{
"name": "MachineName",
"value": "DESKTOP-OS52032"
},
{
"name": "ProcessId",
"value": 16716
},
{
"name": "ThreadId",
"value": 1
},
{
"name": "ApplicationSource",
"value": "WebApp-RAZOR"
}
],
"messageTemplateTokens": [
{
"propertyName": "LogEventCategory",
"rawText": "{#LogEventCategory}"
},
{
"propertyName": "LogEventType",
"rawText": "{#LogEventType}"
},
{
"propertyName": "LogEventSource",
"rawText": "{#LogEventSource}"
},
{
"propertyName": "LogUserId",
"rawText": "{#LogUserId}"
},
{
"propertyName": "LogUsername",
"rawText": "{#LogUsername}"
},
{
"propertyName": "LogForename",
"rawText": "{#LogForename}"
},
{
"propertyName": "LogSurname",
"rawText": "{#LogSurname}"
},
{
"propertyName": "LogData",
"rawText": "{#LogData}"
}
],
"eventType": "$A970522D",
"level": "Information",
"renderedMessage": "SystemApplication StartupWebApp_RAZORApplication Starting Up",
"id": "event-1a29db8ad2c608d8079f040000000000",
"links": {
"Self": "api/events/event-1a29db8ad2c608d8079f040000000000{?download,render,clef}",
"Group": "api/events/resources"
}
}
]
}
Below is the model created from using the paste special control:
public class SeqLogEvents
{
public class Rootobject
{
public string LastReadEventId { get; set; }
public int ScannedEventCount { get; set; }
public Evententity[] EventEntities { get; set; }
}
public class Evententity
{
public DateTime Timestamp { get; set; }
public Property1[] Properties { get; set; }
public Messagetemplatetoken[] MessageTemplateTokens { get; set; }
public string EventType { get; set; }
public string Level { get; set; }
public string TenderedMessage { get; set; }
public string Id { get; set; }
public Links Links { get; set; }
}
public class Links
{
public string Self { get; set; }
public string Group { get; set; }
}
public class Property1
{
public string Name { get; set; }
public object Value { get; set; }
}
public class Messagetemplatetoken
{
public string PropertyName { get; set; }
public string RawText { get; set; }
}
}
I need to be able to access every property value in each section, included nested items as well as iterating over a list of the above items in a foreach loop. I've done this successfully in the past using Newtonsoft Json but I'm trying to achieve the same using system.text.json as the newer standard.
I can't even get access the first property value. I was trying to follow the MS instructions here:
How to read JSON as .NET objects (deserialize)

My stupid mistake, realised I was trying to reference the wrong class.
Should have referenced the Rootobject when deserializing:

Related

System.Text.Json.JsonException - Error deserializing

Here is my code:
public class Element
{
public string? Group { get; set; }
public int Position { get; set; }
public string? Name { get; set; }
public int Number { get; set; }
[JsonPropertyName("small")]
public string? Sign { get; set; }
public double Molar { get; set; }
public IList<int>? Electrons { get; set; }
public override string ToString()
{
return $"{Sign} - {Name}";
}
}
public List<Element> LoadJson()
{
using (StreamReader r = new StreamReader("jsondata/elements.json"))
{
string json = r.ReadToEnd();
var ElementObject = JsonSerializer.Deserialize<List<Element>>(json);
return ElementObject;
}
}
elements.json
{
"elements": [
{
"group": "Other",
"position": 0,
"name": "Hydrogen",
"number": 1,
"small": "H",
"molar": 1.00794,
"electrons": [
1
]
},
{
"group": "Noble Gas (p)",
"position": 17,
"name": "Helium",
"number": 2,
"small": "He",
"molar": 4.002602,
"electrons": [
2
]
}
]
}
while I am trying to deserialize I get
System.Text.Json.JsonException: 'The JSON value could not be converted to System.Collections.Generic.List`1[BlazorApp4.Shared.Entities.Element]. Path: $ | LineNumber: 0 |
What am I missing?
The JSON doesn't represent a List<Element>. It represents an object which has a property which is a List<Element>.
Create that object:
public class Root
{
public List<Element> Elements { get; set; }
}
And deserialize into that:
var root = JsonSerializer.Deserialize<Root>(json);
return root.Elements;

NSwag generates base class even though JsonSchemaFlattenAttribute is set

I am using NSwag for my ASP.NET Core web api project and just can't get it to work. What I'm trying to do is to exclude some of the base properties of IdentityUser. Here is my custom user class:
[DataContract]
[JsonSchemaFlattenAttribute]
public class User : IdentityUser
{
// Overridden variables
[DataMember( Name = "id" )]
[PersonalData]
public new string Id { get; set; }
[DataMember( Name = "email" )]
[ProtectedPersonalData]
public new string Email { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new DateTimeOffset? LockoutEnd { get; set; }
[PersonalData]
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new bool TwoFactorEnabled { get; set; }
[PersonalData]
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new bool PhoneNumberConfirmed { get; set; }
[ProtectedPersonalData]
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new string PhoneNumber { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new string ConcurrencyStamp { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new string SecurityStamp { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new string PasswordHash { get; set; }
[PersonalData]
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new bool EmailConfirmed { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new string NormalizedEmail { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new string NormalizedUserName { get; set; }
[ProtectedPersonalData]
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new string UserName { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new bool LockoutEnabled { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public new int AccessFailedCount { get; set; }
// Custom variables
[DataMember( Name = "date_joined_utc" )]
public DateTime DateJoinedUtc { get; set; }
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public virtual ICollection<RefreshToken> RefreshTokens { get; set; }
// Optimistic concurrency
[Timestamp]
[IgnoreDataMember]
[JsonSchemaIgnoreAttribute]
public byte[] RowVersion { get; set; }
}
Which generates:
"User": {
"type": "object",
"required": [
"dateJoinedUtc"
],
"properties": {
"id": {
"type": "string"
},
"email": {
"type": "string"
},
"dateJoinedUtc": {
"type": "string",
"format": "date-time"
}
},
"allOf": [
{
"$ref": "#/definitions/IdentityUserOfString"
},
{}
]
},
"IdentityUserOfString": {
"type": "object",
"required": [
"emailConfirmed",
"phoneNumberConfirmed",
"twoFactorEnabled",
"lockoutEnabled",
"accessFailedCount"
],
"properties": {
"id": {
"type": "string"
},
"userName": {
"type": "string"
},
"normalizedUserName": {
"type": "string"
},
"email": {
"type": "string"
},
"normalizedEmail": {
"type": "string"
},
"emailConfirmed": {
"type": "boolean"
},
"passwordHash": {
"type": "string"
},
"securityStamp": {
"type": "string"
},
"concurrencyStamp": {
"type": "string"
},
"phoneNumber": {
"type": "string"
},
"phoneNumberConfirmed": {
"type": "boolean"
},
"twoFactorEnabled": {
"type": "boolean"
},
"lockoutEnd": {
"type": "string",
"format": "date-time"
},
"lockoutEnabled": {
"type": "boolean"
},
"accessFailedCount": {
"type": "integer",
"format": "int32"
}
}
},
My understanding of JsonSchemaFlattenAttribute is that it should exclude the base classes and put every property in the derived class. So why is the class IdentityUserOfString generated? What am I missing?
For some reason setting OpenApiIgnore on the User class solved it:
[DataContract]
//[JsonSchemaFlattenAttribute]
[OpenApiIgnore]
public class User : IdentityUser
Generates:
"User": {
"type": "object",
"required": [
"dateJoinedUtc"
],
"properties": {
"id": {
"type": "string"
},
"email": {
"type": "string"
},
"dateJoinedUtc": {
"type": "string",
"format": "date-time"
}
}
},

QnAMaker QueryResult Missing Context Property

I'm working on adding follow up prompts to QnAMaker responses.
var answers = await qnaService.GetAnswersAsync(stepContext.Context, null, null);
var prompts = answers[0].Context?.morecodehere() <--- Context not defined.
answers is resolved as QueryResult[]
The documentation, samples, and SO question indicate that there is a Context property on the QueryResult object; however, my project's QnAResult from the Virtual Assistant Template does not.
I'd like to be able to follow the samples's way of accessing follow-up prompts using adaptive cards.
My question is whether or not I have something outdated or if it is more likely that I have a configuration issue. Could this be as simple as creating a new QueryResult model to use?
Sample from Microsoft's sample library:
...
var query = inputActivity.Text;
var qnaResult = await _qnaService.QueryQnAServiceAsync(query, (QnABotState)oldState);
var qnaAnswer = qnaResult[0].Answer;
var prompts = qnaResult[0].Context?.Prompts;
if (prompts == null || prompts.Length < 1)
{
outputActivity = MessageFactory.Text(qnaAnswer);
}
...
QueryResult Class from Metadata Locally
namespace Microsoft.Bot.Builder.AI.QnA
{
public class QueryResult
{
public QueryResult();
[JsonProperty("questions")]
public string[] Questions { get; set; }
[JsonProperty("answer")]
public string Answer { get; set; }
[JsonProperty("score")]
public float Score { get; set; }
[JsonProperty(PropertyName = "metadata")]
public Metadata[] Metadata { get; set; }
[JsonProperty(PropertyName = "source")]
public string Source { get; set; }
[JsonProperty(PropertyName = "id")]
public int Id { get; set; }
}
}
Sample from Faq.qna Json
"qnaDocuments": [
{
"id": 46,
"answer": "Answer Text [Redacted Link](https://link)\n.",
"source": "sourcefile.docx",
"questions": [
"Sample question text"
],
"metadata": [],
"alternateQuestions": "",
"alternateQuestionClusters": [],
"context": {
"isContextOnly": false,
"prompts": [
{
"displayOrder": 0,
"qnaId": 51,
"qna": {
"id": 51,
"answer": "[Redacted Link](https://linkhere)",
"source": "Editorial",
"questions": [
"More Information",
"Troubleshooting Information"
],
"metadata": [],
"alternateQuestions": "",
"alternateQuestionClusters": [],
"context": {
"isContextOnly": true,
"prompts": []
}
},
"displayText": "More Information"
}
]
}
},
...

WCF REST service dictionary serialization

I'm having trouble with serializing dictionary in my WCF service.
[DataContract]
public class UserInfo
{
[DataMember]
public Guid ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public IDictionary<string, List<string>> Permissions { get; set; } = new Dictionary<string, List<string>>();
}
This is example of current response
{
"ID": "1",
"Name": "admin",
"Permissions": [
{
"Key": "Users",
"Value": [
"Read",
"Edit"
]
},
{
"Key": "Management",
"Value": [
"Read"
]
}
]
}
and this is desired response
{
"ID": "1",
"Name": "admin",
"Permissions": {
"Users": ["Read", "Edit"],
"Management": ["Read"]
}
}
Is there way to implement this globally or on specific property?
I've solved it by using custom made dictionary as explained here.
Since I've created this type for serialization
AjaxDictionary<string, string[]>
I had to add
[ServiceKnownType(typeof(string[]))]
on my class that is ServiceContract for returning responses.

How to display Swashbuckle parameter object only with fields that should actually be sent?

I'm starting to work with Swagger using the Swashbuckle library for AspNetCore.
And when putting in my API an object with references it presents as if it were necessary to send all the fields of the references, when only the identifier (Id)
Here's an example:
Model Structure:
public class Cidade
{
public long Id { get; set; }
public string Nome { get; set; }
public Uf Uf { get; set; }
}
public class Uf
{
public long Id { get; set; }
public string Nome { get; set; }
public Pais Pais { get; set; }
}
public class Pais
{
public long Id { get; set; }
public string Nome { get; set; }
}
And the following API:
[Produces("application/json")]
[Route("api/Cidade")]
public class CidadeController : Controller
{
// POST: api/Cidade
[HttpPost]
public void Post([FromBody]Cidade value)
{
}
}
The result in Swagger is as follows:
And what I would like is the following (only up to uf.id):
How can I get this result?
I followed the logic of #HelderSepu answer, to get my solution, which would be as follows:
I built a Schema filter to add an example to the reference properties (Ref), which has a property called "Id":
public class ApplySchemaRefIdExtensions : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
if (schema.Properties != null)
{
foreach (var p in schema.Properties)
{
if (p.Value.Example == null && p.Value.Ref != null)
{
var reference = context.SystemType.GetProperty(p.Value.Ref.Split("/").LastOrDefault());
if (reference != null)
{
var id = reference.PropertyType.GetProperty("Id");
if (id != null)
{
p.Value.Example = new
{
Id = 123
};
p.Value.Ref = null;
}
}
}
}
}
}
}
On Startup.cs:
services.AddSwaggerGen(c =>
{
// ...
c.SchemaFilter<ApplySchemaRefIdExtensions>();
});
Result for the same example of the question:
I was looking on my samples and I think I found something you can use:
http://swagger-net-test.azurewebsites.net/swagger/ui/index?filter=P#/PolygonVolume/PolygonVolume_Post
On my case I'm adding more, you need less, but still what you need is just a custom example...
the JSON looks like this:
"PolygonVolumeInsideParameter": {
"properties": {
"Points": {
"items": {
"$ref": "#/definitions/Location"
},
"xml": {
"name": "Location",
"wrapped": true
},
"example": [
{
"Lat": 1.0,
"Lon": 2.0
},
{
"Lat": 5.0,
"Lon": 6.0
}
],
"type": "array"
},
"PlanId": {
"type": "string"
}
},
"xml": {
"name": "PolygonVolumeInsideParameter"
},
"type": "object"
},
And on swashbuckle I added the example it with an ISchemaFilter my code is here:
https://github.com/heldersepu/Swagger-Net-Test/blob/master/Swagger_Test/App_Start/SwaggerConfig.cs#L891