NSwag generates base class even though JsonSchemaFlattenAttribute is set - asp.net-core

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"
}
}
},

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;

System.Text.Json Deserialize and get Property Values [closed]

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:

How to define the name of the data for the ajax.datatable column name? I keep getting the unknow parameter error for ajax.datatable

JobProg.js
var dataTable;
$(document).ready(function () {
loadDataTable();
});
function loadDataTable() {
dataTable = $('#tblData').DataTable({
"ajax": {
"url": "/Admin/CMJobProg/GetAll"
},
"columns": [
{ "data": "jobnumber", "width": "35%" },
{ "data": "assignfrom", "width": "35%" },
{ "data": "assignto", "width": "35%" },
{ "data": "dateassign", "width": "35%" },
{ "data": "jobaction", "width": "35%" },
{ "data": "remarks", "width": "35%" },
{ "data": "type", "width": "35%" },
{ "data": "division" ,"width": "35%" }
]
})
}
CMJobProg.cs
namespace LXG.Models
{
[Table("CMJOBPROG", Schema = "LASIS")]
public class CMJobProg
{
[Required]
[MaxLength(8)]
[Display(Name = "Job Number")]
[Column("JOB_NUMBER")]
public string JobNumber { get; set; }
[Display(Name = "Assign From")]
[MaxLength(22)]
[Column("ASSIGN_FROM")]
public string AssignFrom { get; set; }
[Display(Name = "Assign To")]
[MaxLength(22)]
[Column("ASSIGN_TO")]
public string AssignTo { get; set; }
[Column("DATE_ASSIGN")]
public DateTime DateAssign { get; set; }
[Display(Name = "Job Action")]
[Range(0, 999)]
[Column("JOB_ACTION")]
public int? JobAction { get; set; }
[Display(Name = "Remarks")]
[MaxLength(500)]
[Column("REMARKS")]
public string Remarks { get; set; }
[Display(Name = "Job Type")]
[Required]
[MaxLength(2)]
[Column("TYPE")]
public string Type { get; set; }
[MaxLength(2)]
[Column("DIVISION")]
public string Division { get; set; }
}
}
DataTables warning: table id=tblData - Requested unknown parameter 'jobnumber' for row 0, column 0. For more information about this error, please see http://datatables.net/tn/4
I keep getting this error, how to fix this? I able to get the data remarks, type and division, but i cant get others data. Acutally how to define the name for the data name?
As far as I know, asp.net core returned json will has a special format. Like this : JobNumber to jobNumber. To solve this issue. I suggest you could set a JsonPropertyName attribute for each model property.
More details, you could refer to below codes:
[Table("CMJOBPROG", Schema = "LASIS")]
public class CMJobProgress
{
[Required]
[MaxLength(8)]
[Display(Name = "Job Number")]
[Column("JOB_NUMBER")]
[JsonPropertyName("jobnumber")]
public string JobNumber { get; set; }
[Display(Name = "Assign From")]
[MaxLength(22)]
[Column("ASSIGN_FROM")]
[JsonPropertyName("assignfrom")]
public string AssignFrom { get; set; }
[Display(Name = "Assign To")]
[MaxLength(22)]
[Column("ASSIGN_TO")]
[JsonPropertyName("assignto")]
public string AssignTo { get; set; }
[Column("DATE_ASSIGN")]
[JsonPropertyName("dateassign")]
public DateTime DateAssign { get; set; }
[Display(Name = "Job Action")]
[Range(0, 999)]
[Column("JOB_ACTION")]
[JsonPropertyName("jobaction")]
public string JobAction { get; set; }
[Display(Name = "Remarks")]
[MaxLength(500)]
[Column("REMARKS")]
[JsonPropertyName("remarks")]
public string Remarks { get; set; }
[Display(Name = "Job Type")]
[Required]
[MaxLength(2)]
[Column("TYPE")]
[JsonPropertyName("type")]
public string Type { get; set; }
[MaxLength(2)]
[Column("DIVISION")]
[JsonPropertyName("division")]
public string Division { get; set; }
}
Result:
Follow case-sensitive column name in your model/entity class. For example,
"data": "jobnumber"
to
"data": "JobNumber"
Add this "AddNewtonsoftJson" in Startup.cs
services.AddControllersWithViews().AddRazorRuntimeCompilation().AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});

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.

ASP.NET core POST request fail

I have a model:
public class CoreGoal
{
[Key]
public long CoreGoalId { get; set; }
public string Title { get; set; }
public string Effect { get; set; }
public string Target_Audience { get; set; }
public string Infrastructure { get; set; }
public virtual ICollection<Benefit> Benefits { get; set; }
public virtual ICollection<Step> Steps { get; set; }
public virtual ICollection<Image> Images { get; set; }
public virtual ICollection<SubGoal> SubGoals { get; set; }
public CoreGoal()
{
}
}
And Image model is as following:
public class Image
{
[Key]
public long ImagelId { get; set; }
public byte[] Base64 { get; set; }
[ForeignKey("CoreGoalId")]
public long CoreGoalId { get; set; }
public Image()
{
}
}
My controller class:
[Route("api/[controller]")]
public class CoreGoalController : Controller
{
private readonly ICoreGoalRepository _coreGoalRepository;
//Controller
public CoreGoalController(ICoreGoalRepository coreGoalRepository) {
_coreGoalRepository = coreGoalRepository;
}
//Get methods
[HttpGet]
public IEnumerable<CoreGoal> GetAll()
{
return _coreGoalRepository.GetAllCoreGoals();
}
[HttpGet("{id}", Name = "GetCoreGoal")]
public IActionResult GetById(long id)
{
var item = _coreGoalRepository.Find(id);
if (item == null)
{
return NotFound();
}
return new ObjectResult(item);
}
//Create
[HttpPost]
public IActionResult Create([FromBody] CoreGoal item)
{
if (item == null)
{
return BadRequest();
}
_coreGoalRepository.CreateCoreGoal(item);
return CreatedAtRoute("GetCoreGoal", new { id = item.CoreGoalId }, item);
}
}
Repository:
public class CoreGoalRepository : ICoreGoalRepository
{
private readonly WebAPIDataContext _db;
public CoreGoalRepository(WebAPIDataContext db)
{
_db = db;
}
//Add new
public void CreateCoreGoal(CoreGoal coreGoal)
{
_db.CoreGoals.Add(coreGoal);
_db.SaveChanges();
}
//Get all
public IEnumerable<CoreGoal> GetAllCoreGoals()
{
return _db.CoreGoals
.Include(coreGoal => coreGoal.Benefits)
.Include(coreGoal => coreGoal.Steps)
.Include(coreGoal => coreGoal.Images)
.Include(coreGoal => coreGoal.SubGoals)
.ToList();
}
//Find specific
public CoreGoal Find(long key)
{
return _db.CoreGoals.FirstOrDefault(t => t.CoreGoalId == key);
}
}
public interface ICoreGoalRepository
{
void CreateCoreGoal(CoreGoal coreGoal);
IEnumerable<CoreGoal> GetAllCoreGoals();
CoreGoal Find(long key);
void DeleteCoreGoal(long id);
void UpdateCoreGoal(CoreGoal coreGoal);
}
When I do a POST request from swagger I get a template like:
{
"coreGoalId": 0,
"title": "string",
"effect": "string",
"target_Audience": "string",
"infrastructure": "string",
"benefits": [
{
"benefitId": 0,
"what": "string",
"coreGoalId": 0
}
],
"steps": [
{
"stepId": 0,
"what": "string",
"coreGoalId": 0
}
],
"images": [
{
"imagelId": 0,
"base64": "string",
"coreGoalId": 0
}
],
"subGoals": [
{
"subGoalId": 0,
"title": "string",
"priority": "string",
"audience": "string",
"social_aspects": "string",
"coreGoalId": 0,
"issues": [
{
"issueId": 0,
"title": "string",
"subGoalID": 0
}
]
}
]
}
If I POST like like this, my request fails with status 400, however if I remove
"images": [
{
"imagelId": 0,
"base64": "string",
"coreGoalId": 0
}
],
from this request, then it is successful. Why is it happening? All other models i.e. Benefit, Step are exactly identical to Image in structure.
UPDATE:
Changing base64 type from byte[] to string eliminates this problem but in that case while saving to my MySql database the big base64 string is chopped and kind of becomes useless to again form the image.