I have two classes that have relations between them. They are Market and Promotion classes. I'm facing a problem when I make a request. The json result stops when it comes to the relation.
The Market class:
public class Market : BaseModel
{
[Required]
public string Name { get; set; }
[Required]
public string Address { get; set; }
// GPS informations.
public double Latitude { get; set; }
public double Longitude { get; set; }
[Required]
public Guid PictureId { get; set; }
public Picture Picture { get; set; }
public List<Promotion> Promotions { get; set; }
}
The Promotion class:
public class Promotion : BaseModel
{
[Required]
public string Description { get; set; }
[Required]
public double Price { get; set; }
[Required]
public Guid PictureId { get; set; }
public Picture Picture { get; set; }
[Required]
public Guid MarketId { get; set; }
public Market Market { get; set; }
}
When I make the next request, I got an incomplete answer.
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<Market>>> Get()
{
var markets = await _context.Markets
.Include(m => m.Owner)
.Include(m => m.Picture)
.Include(m => m.Promotions)
.ToListAsync();
return markets;
}
The response json stops when get at MarketId of the first promotion.
...
"pictureType": 0,
"pictureUrl": "https://superbarato.azurewebsites.net/api/Pictures/url/d6bc07a8-db55-4ee5-7342-08d73f6147e9",
"id": "d6bc07a8-db55-4ee5-7342-08d73f6147e9",
"createdAt": "2019-09-22T13:34:26.9367403",
"updatedAt": "0001-01-01T00:00:00",
"deletedAt": "0001-01-01T00:00:00",
"ownerId": "75c1f286-c07f-4e50-dda0-08d73f61058f",
"owner": null
},
"promotions": [
{
"description": "Açúcar Camil 1Kg",
"price": 5.0,
"pictureId": "e7af68b9-c053-4f4b-7344-08d73f6147e9",
"picture": null,
"marketId": "e2962be8-1a19-418a-6ce7-08d73f62308d"
How to get all the promotions?
In EF Core , you could configure Json.NET to ignore cycles that it finds in the object graph. This is done in the ConfigureServices(...) method in Startup.cs.
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
Another alternative is to decorate one of the navigation properties with the [JsonIgnore] attribute, which instructs Json.NET to not traverse that navigation property while serializing.
Reference :https://learn.microsoft.com/en-us/ef/core/querying/related-data#related-data-and-serialization
The problem of yours is that Market and Promotion types/instances are referencing each other and you got into a cycle serializing those two indefinitely. You may solve it by projecting database model without relationship to response model/structure to avoid that.
[HttpGet]
[AllowAnonymous]
public async Task<ActionResult<IEnumerable<MarketModel>>> Get()
{
var markets = await _context.Markets
.Select(m => new MarketModel {
Name = m.Name,
// Other properties needed to be serialized to response body
Promotions = m.Promotions.Select(p => new PromotionModel {
Description = p.Description,
// Other properties needed to be serialized to response body
MarketId = p.Market.Id
}
}
.ToListAsync();
return markets;
}
public class MarketModel
{
public string Name { get; set; }
// Other properties needed to be serialized to response body
public List<PromotionModel> Promotions { get; set; }
}
public class PromotionModel
{
public string Description { get; set; }
// Other properties needed to be serialized to response body
public Guid MarketId { get; set; }
}
Hope it helps.
Related
Everything looks good but I don't understand why I still get an error like:
"System.InvalidCastException: The field of type DotnetCoreWebAPI.Enum+Blood must be a string, array or ICollection type."
I've left some meaningful snippets of code and the error I'm getting, below.
Model:
public class User
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string Name { get; set; }
[MaxLength(100)]
public string Surname { get; set; }
[MaxLength(2)]
public Enum.Blood Blood { get; set; }
[MaxLength(50)]
public string Cellphone { get; set; }
[MaxLength(500)]
public string Adress { get; set; }
}
Enum:
public class Enum
{
public enum Blood
{
ARhDpositive,
ARhDnegative,
BRhDpositive,
BRhDnegative,
ORhDpositive,
ORhDnegative,
ABRhDpositive,
ABRhDnegative
}
}
Controller:
[HttpPost]
public ActionResult<UserReadDto> CreateUser(User userCreateDto)
{
var userModel = _mapper.Map<User>(userCreateDto);
_repository.CreateUser(userModel);
return Ok(userModel);
}
Service Configurations:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<UserContext>(opt => opt.UseSqlServer
(Configuration.GetConnectionString("DatabaseConnection")));
services.AddControllers().AddJsonOptions(opt =>
opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()));
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddScoped<IUserRepo, SqlUserRepo>();
}
Note:
I use Postman when I test api
I found out why problem occurs, I've wrote User instead of UserCreateDto in this line:
public ActionResult<UserReadDto> CreateUser(**UserCreateDto ** userCreateDto)
I have a JSON file that is pretty complex. Here is a snippet of my file:
JSON
"SKU": "12345",
"Status": {
"Health": "OK"
},
"Type": "ComputerSystem",
"Name": "Cartridge 1",
"Power": "Off",
"AssetTag": "12345",
"HostCorrelation": {
"IPAddress": [],
"HostMACAddress": [
"00:00:00:00:00:00",
"11:11:11:11:11:11"
]
},
"SerialNumber": "12345",
"Boot": {
"BootSourceOverrideSupported": [
"None",
"PXE",
"HDD",
"iSCSI",
"M.2",
"None"
],
"BootSourceOverrideTarget": "PXE",
"BootSourceOverrideEnabled": "Continuous"
}
Without showing all the classes here is the RootObject VS generates as code:
Paste JSON as Class
public class Rootobject
{
public string SKU { get; set; }
public Status Status { get; set; }
public string Type { get; set; }
public string Name { get; set; }
public string Power { get; set; }
public string AssetTag { get; set; }
public Hostcorrelation HostCorrelation { get; set; }
public string SerialNumber { get; set; }
public Boot Boot { get; set; }
public Links links { get; set; }
public string UUID { get; set; }
public Bios Bios { get; set; }
public Oem Oem { get; set; }
public Memory Memory { get; set; }
public Availableaction[] AvailableActions { get; set; }
public string SystemType { get; set; }
public Processors Processors { get; set; }
public string Model { get; set; }
public string Manufacturer { get; set; }
}
I want to loop through multiple JSON files with this structure, and put them into a few columns such as (Section, Component, Property, and Value).
However, I have been having a hard time figuring this out. It would be simple to put each part into its own unique column.
The end result of my JSON example above may look like:
Goal SQL Output
The format doesn't have to be exact, but something along those lines. If there is a better way of doing this I am all ears.
I can tell you didn't post all of your classes because the RootObject has object references, but this is how you could start your code. This won;t get your data into the format you asked for, but it is how the serializer works.
string json = [Somehow get your json in to a string]
JavaScriptSerializer js = new JavaScriptSerializer();
var jRow = js.Deserialize<Rootobject>(json);
// now you have your entire JSON in one object.
//for the data you presented you will need a few outputs:
// let's start with the outermost:
Output0Buffer.AddRow();
Output0Buffer.SKU = jRow.SKU;
Output0Buffer.Health = jRow.Status.Health; //There is only one option here
Output0Buffer.Type = jRow.Type ;
Output0Buffer.Name = jRow.Name;
Output0Buffer.Power = jRow.Power ;
Output0Buffer.AssetTag = jRow.AssetTag ;
Output0Buffer.SerialNumber = jRow.SerialNumber ;
Output0Buffer.BootSourceOverrideTarget= jRow.Boot.BootSourceOverrideTarget ;
Output0Buffer.BootSourceOverrideEnabled= jRow.Boot.BootSourceOverrideEnabled;
//this is a new output of boot details linked by SKU
foreach(var dtl in jRow.Boot.BootSourceOverrideSupported)
{
OutputBootStuffBuffer.AddRow();
OutputBootStuffBuffer.SKU = JRow.SKU; //Making assumption that SKU is the key back
OutputBootStuffBuffer.BootSourceOverrideSupported = dtl.BootSourceOverrideSupported;
}
I'm building REST API server in .NET core. I'm testing my code via Postman software. I have a problem with Include() method that enables me to attach navigation property data. I'm trying to get data in [HttpGet] action and objects that are being returned are wrong.
My code :
MODELS
Session model
public class Session
{
[Key]
public int IDSession { get; set; }
[Required]
public DateTime LogInTime { get; set; }
public DateTime LogOutTime { get; set; }
[Required]
public int IDUser { get; set; }
public User User { get; set; }
[Required]
public int IDMachine { get; set; }
public Machine Machine { get; set; }
}
User model
public class User
{
[Key]
public int IDUser { get; set; }
[Required]
public string Forename { get; set; }
[Required]
public string Name { get; set; }
public string AvatarPath { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
public string Password { get; set; }
public User CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
public List<UserGroup> UsersGroups { get; set; }
public List<Alarm> ExecutedAlarms { get; set; }
public List<Alarm> ResetedAlarms { get; set; }
public List<AccessCard> Cards { get; set; }
public List<AccessCard> UserCardsAdded { get; set; }
public List<User> UsersAdded { get; set; }
public List<Session> Sessions { get; set; }
public List<EventsLog> Events { get; set; }
public List<Reference> References { get; set; }
public List<UserPermission> UsersPermissions { get; set; }
}
Session controller
[Produces("application/json")]
[Route("api/Sessions")]
public class SessionsController : Controller
{
private readonly DBContext _context;
#region CONSTRUCTOR
public SessionsController(DBContext context)
{
_context = context;
}
#endregion
#region HTTP GET
// GET: api/sessions
[HttpGet]
public async Task<IActionResult> GetSessions()
{
var sessions = await _context.Sessions.Include(s => s.User). ToListAsync();
if (sessions.Any())
{
return new ObjectResult(sessions);
}
else
{
return NotFound();
}
}
// GET: api/sessions/1
[HttpGet("{id}", Name = "GetSessionByID")]
public async Task<IActionResult> GetSessionByID(Int32 id)
{
var session = await _context.Sessions.Include(s => s.User).FirstOrDefaultAsync(s => s.IDSession == id);
if (session == null)
{
return NotFound();
}
else
{
return new ObjectResult(session);
}
}
#endregion
}
The idea is that User model contains List<Session> collection that he/she created. I want to be able to return users with its sessions
Of course Session model contains a single User because every session is related with a specific, single User.
Now, when I need to get all sessions objects in SessionController with GetSessions() or GetSessionsByID() I use POSTMAN [HttpGet] action like this : http://localhost:8080/api/sessions which returns me wrong data :
A session contains a user and in turn a single user is related with its sessions. It looks like it tries to return me Session object properly, includes User object but then it tries to include all sessions for that User. That's not what I want. It looks like some kind of a loop. Sessions shoud be returned with its User objects and that's it. How can I achieve that ? Am I doing some logical mistake in my models ?
Thanks !
I met also this issue recently. So, I've fixed it by adding this script in the Startup.cs file and ConfigureServices method :
services.AddMvc().AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
So, you suffix services.AddMvc() by this code who means that you have to make JSON.Net to ignore cycles finded to the nested object request. And of course having Newtonsoft.Json package installed to your project and referenced in each concerned file
For much clearer information, see this link at Related Data and Serialization section :
https://learn.microsoft.com/en-us/ef/core/querying/related-data
Hope this is helpfull for you
[DataContract]
public partial class Event
{
[DataMember]
public int EventId { get; set; }
[DataMember]
[Required]
[StringLength(50)]
public string UserId { get; set; }
[DataMember]
[Required]
public string EventDescription { get; set; }
[DataMember]
[Column(TypeName = "smalldatetime")]
public DateTime Timestamp { get; set; }
[DataMember]
public int ActivityId { get; set; }
[DataMember]
public DbGeography Location { get; set; }
[IgnoreDataMember]
public virtual Activity Activity { get; set; }
}
I have the above class in web api and my get all and get by id functions work. Here is an example of the json they return:
{
"EventId": 6,
"UserId": "1",
"EventDescription": "Auckland city test",
"Timestamp": "2015-02-11T00:00:00",
"ActivityId": 1,
"Location": {
"Geography": {
"CoordinateSystemId": 4326,
"WellKnownText": "POINT (180.756117 -30.85824)"
}
}
}
However if i pass the same json back to the post method it is not able to convert the location to a dbgeography type. I have tried formatting the location in different ways but always get a nullReferenceException in location.
Is there a particular way I should format the location in json? I would have thought that if it serializes to that format it would be able to de-serialize that format.
Thanks
I want to parse a json to List how can we do that. I have tried the following code but it didnt worked
Dictionary<string, object> pGateways=(Dictionary<string,object>)Json.JsonParser.FromJson(jsonString);
List<object> creditOptions = new List<object>();
creditOptions = (List<object>)pGateways;
And after getting it int list i want to loop through it
Here is my sample json
{
"MessageCode": "CS2009",
"Status": "Y",
"ErrorCode": "0",
"ErrorDescription": "Success",
"account":
{
"card":
[
{
"cardend": "asd",
"token": "aads",
"cardstart": "asdad",
"accounttype": "asda",
"cardnetwork": "as",
"issuer": "asd",
"customername": "a",
"expdate": "04/2018"
},
{
"cardend": "asda",
"token":"adssadsa",
"cardstart": "asd",
"accounttype": "asd",
"cardnetwork": "asd",
"issuer": "asda",
"customername": "asd",
"expdate": "03/2016"
}
],
"bank": []
}
}
The best option could be to use the JsonConvert in order to parse Json into a List.
Reference: JSON Parsing in Windows Phone
You can use Json.Net.
To install Json.NET use NugetGallery : Json.net Nugets Gallery
And you can use json2Csharp.com for generate c# classes from json
The JSON string you posted is not suitable for straight-forward deserialization to List. The easiest thing to do is use the online JSON 2 CSharp tool to generate classes and deserialize the json string to it. Here is an example of the generated classes:
public class Card
{
public string cardend { get; set; }
public string token { get; set; }
public string cardstart { get; set; }
public string accounttype { get; set; }
public string cardnetwork { get; set; }
public string issuer { get; set; }
public string customername { get; set; }
public string expdate { get; set; }
}
public class Account
{
public List<Card> card { get; set; }
public List<object> bank { get; set; }
}
public class RootObject
{
public string MessageCode { get; set; }
public string Status { get; set; }
public string ErrorCode { get; set; }
public string ErrorDescription { get; set; }
public Account account { get; set; }
}
And here is the logic for deserialization:
var root = JsonConvert.DeserializeObject<RootObject>(jsonStr);
where the jsonStr variable holds the json string you posted.
You need to use json2csharp tool to generate classes and deserialize the JSON string to list.
Here is the Classes generated from your JSON string.
public class Card
{
public string cardend { get; set; }
public string token { get; set; }
public string cardstart { get; set; }
public string accounttype { get; set; }
public string cardnetwork { get; set; }
public string issuer { get; set; }
public string customername { get; set; }
public string expdate { get; set; }
}
public class Account
{
public List<Card> card { get; set; }
public List<object> bank { get; set; }
}
public class RootObject
{
public string MessageCode { get; set; }
public string Status { get; set; }
public string ErrorCode { get; set; }
public string ErrorDescription { get; set; }
public Account account { get; set; }
}
and Deserialize your JSON object using JsonConvert,
Suppose e.result is your JSON string then
var rootObject = JsonConvert.DeserializeObject<RootObject>(e.Result);
foreach (var blog in rootObject.Card)
{
//access your data like this- `blog.cardend;` or `blog.token;`
}