I'm using Ef 4.1 and I've got a POCO object I'd like to serialize to JSON, I've read there is a problem to do so when using lazy loading but I'm not sure I can because a Message can have a collection of Message.
Is there any way to do this? sirialize this kind of object into JSON?
My Message object looks like:
public class Message
{
[Key]
public int Id { get; set; }
public int? ParentId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreatedAt { get; set; }
public DateTime? LastModified { get; set; }
public virtual User User { get; set; }
public virtual Message Parent { get; set; }
public virtual ICollection<Message> Children { get; set; }
}
The problem is circular references. An easy way to avoid this is to use Json.Net http://james.newtonking.com/projects/json-net.aspx instead of the default MVC json serializer. The latest version of Json.Net will serialize objects with circular references out of the box. http://james.newtonking.com/projects/json/help/PreserveObjectReferences.html for more info on the problem
Eager load it using Include(). Sample linq:
var serializeMe = (from m in MyContext.Message.Include("User") where m.Id == someValue select m).ToList();
That will tell EF to load the User navigation property right away instead of lazy loading it, and the serializer should have no problem with it then.
How about this:
Mark your class as [Serializable]
Use the JsonSerializer to serialize your object to JSON.
Perhaps use eager loading on the properties in your EF query?
Message msg = new Message();
var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(msg.GetType());
MemoryStream ms = new MemoryStream();
serializer.WriteObject(ms, msg);
string json = Encoding.Default.GetString(ms.ToArray());
[Serializable]
public class Message
{
}
Well, lets go by parts.
¿Why is happening this?
Because you have virtual properties. If you are using EF you actually need them if you are using Lazy loading. You can configurate your EF to not do this by this example:
context.Configuration.ProxyCreationEnabled = false;
where context is your ObjectContext or DbContext... this assuming you are using EF. But for most scenarios this is not a good aproach.
Possible Solution
As I always say: "there are not good or bad solutions, just different ways and it depends on the context", saying that, you can create dynamic objects.
In case you only have to serialize a unique object, you can do something like this
Json(new {#property1=yourObject.property1, #property2=yourObject.property2})
In case you have a list, well, you can do this:
var list = new List<dynamic>();
foreach(var item in myRepository.GetAll())
{
list.Add(new
{
#property1= item.property1,
#property2= item.property2,
#property3= item.property3
});
}
return Json(list, JsonRequestBehavior.DenyGet);
I tried to make this as generic as I can. I hope this can help somebody!!
Best regards and have a very nice day! :)
Related
I am converting my newtonsoft implementation to new JSON library in .net core 3.0. I have the following code
public static bool IsValidJson(string json)
{
try
{
JObject.Parse(json);
return true;
}
catch (Exception ex)
{
Logger.ErrorFormat("Invalid Json Received {0}", json);
Logger.Fatal(ex.Message);
return false;
}
}
I am not able to find any equivalent for JObject.Parse(json);
Also what will be the attribute JsonProperty equivalent
public class ResponseJson
{
[JsonProperty(PropertyName = "status")]
public bool Status { get; set; }
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
[JsonProperty(PropertyName = "Log_id")]
public string LogId { get; set; }
[JsonProperty(PropertyName = "Log_status")]
public string LogStatus { get; set; }
public string FailureReason { get; set; }
}
One more thing i will be looking for the equivalent of Formating.None.
You are asking a few questions here:
I am not able to find any equivalent for JObject.Parse(json);
You can use JsonDocument to parse and examine any JSON, starting with its RootElement. The root element is of type JsonElement which represents any JSON value (primitive or not) and corresponds to Newtonsoft's JToken.
But do take note of this documentation remark:
This class utilizes resources from pooled memory to minimize the impact of the garbage collector (GC) in high-usage scenarios. Failure to properly dispose this object will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.
When you need to use a JsonElement outside the lifetime of its document, you must clone it:
Gets a JsonElement that can be safely stored beyond the lifetime of the original JsonDocument.
Also note that JsonDocument is currently read-only and does not provide an API for creating or modifying JSON. There is an open issue Issue #39922: Writable Json DOM tracking this.
An example of use is as follows:
//https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#using-declarations
using var doc = JsonDocument.Parse(json);
//Print the property names.
var names = doc.RootElement.EnumerateObject().Select(p => p.Name);
Console.WriteLine("Property names: {0}", string.Join(",", names)); // Property names: status,message,Log_id,Log_status,FailureReason
//Re-serialize with indentation.
using var ms = new MemoryStream();
using (var writer = new Utf8JsonWriter(ms, new JsonWriterOptions { Indented = true }))
{
doc.WriteTo(writer);
}
var json2 = Encoding.UTF8.GetString(ms.GetBuffer(), 0, checked((int)ms.Length));
Console.WriteLine(json2);
Also what will be the attribute JsonProperty equivalent?
Attributes that can control JsonSerializer are placed in the System.Text.Json.Serialization namespace and inherit from an abstract base class JsonAttribute. Unlike JsonProperty, there is no omnibus attribute that can control all aspects of property serialization. Instead there are specific attributes to control specific aspects.
As of .NET Core 3 these include:
[JsonPropertyNameAttribute(string)]:
Specifies the property name that is present in the JSON when serializing and deserializing. This overrides any naming policy specified by JsonNamingPolicy.
This is attribute you want to use to control the serialized names of your ResponseJson class:
public class ResponseJson
{
[JsonPropertyName("status")]
public bool Status { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; }
[JsonPropertyName("Log_id")]
public string LogId { get; set; }
[JsonPropertyName("Log_status")]
public string LogStatus { get; set; }
public string FailureReason { get; set; }
}
[JsonConverterAttribute(Type)]:
When placed on a type, the specified converter will be used unless a compatible converter is added to the JsonSerializerOptions.Converters collection or there is another JsonConverterAttribute on a property of the same type.
Note that the documented priority of converters -- Attribute on property, then the Converters collection in options, then the Attribute on type -- differs from the documented order for Newtonsoft converters, which is the JsonConverter defined by attribute on a member, then the JsonConverter defined by an attribute on a class, and finally any converters passed to the JsonSerializer.
[JsonExtensionDataAttribute] - corresponds to Newtonsoft's [JsonExtensionData].
[JsonIgnoreAttribute] - corresponds to Newtonsoft's [JsonIgnore].
When writing JSON via Utf8JsonWriter, indentation can be controlled by setting JsonWriterOptions.Indented to true or false.
When serializing to JSON via JsonSerializer.Serialize, indentation can be controlled by setting JsonSerializerOptions.WriteIndented to true or false.
Demo fiddle here showing serialization with JsonSerializer and parsing with JsonDocument.
This link should get you going, snippets of which I copied below.
https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
WeatherForecast Deserialize(string json)
{
var options = new JsonSerializerOptions
{
AllowTrailingCommas = true
};
return JsonSerializer.Parse<WeatherForecast>(json, options);
}
class WeatherForecast {
public DateTimeOffset Date { get; set; }
// Always in Celsius.
[JsonPropertyName("temp")]
public int TemperatureC { get; set; }
public string Summary { get; set; }
// Don't serialize this property.
[JsonIgnore]
public bool IsHot => TemperatureC >= 30;
}
I have upgraded my project to netcore 3.0 and I am in the middle of refactoring a project to use the new nullable references types feature, but got stuck pretty quickly because of the following issue.
Lets say I consume a REST api which returns the following JSON:
{
"Name": "Volvo 240",
"Year": 1989
}
This api always returns the name/year, so they are non-nullable.
I would use this simple class for deserialization:
public class Car
{
public string Name {get; set;}
public int Year {get; set;}
}
And I would deserialize this to a Car instance using the new System.Text.Json
var car = JsonSerializer.Deserialize<Car>(json);
This all works, but when enabling nullable reference types I get a warning in the Car class that Name is declared as non-nullable but can be null. I understand why I get this since it is possible to instantiate this object without initializing the Name property.
So ideally Car should look like this:
public class Car
{
public string Name { get; }
public int Year { get; }
public Car(string name, int year)
{
Name = name;
Year = year;
}
}
But this doesn't work because System.Text.Json serializer doesn't support constructors with parameters.
So my question is: How would I declare Car so that Name is non-nullable and get it to work with System.Text.Json without getting "non-nullable" warning?`
I don't want to make it nullable because I would have to do null-checks on basically everything when enabling nullable reference types, and since the REST API in my example says that they are always provided they shouldn't be nullable.
UPDATE
System.Text.Json for .NET 5 now supports parameterized constructors, so this should not be a problem anymore.
See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-immutability?pivots=dotnet-5-0
Old answer below
After reading the msdocs I found out how I could solve this issue.
So until System.Text.Json cannot instantiate classes with parameters in their constructor, the Car class will have to look like this:
public class Car
{
public string Name { get; set; } = default!;
public int Year { get; set; }
}
Update
If you're on net5, use the parameterized constructor support now offer as #langen points out. Else below can still be useful.
Original
Slightly alternative approach. System.Text.Json appears to have no problems using a private parameterless constructor. So you can at least do the following:
public class Car
{
public string Name { get; set; }
public int Year { get; set; }
// For System.Text.Json deserialization only
#pragma warning disable CS8618 // Non-nullable field is uninitialized.
private Car() { }
#pragma warning restore CS8618
public Car(string name, int year)
{
Name = name
?? throw new ArgumentNullException(nameof(name));
Year = year;
}
}
Benefits being:
Init of the object from your own code must be through the public ctor.
You don't need to do = null!; on each property.
Remaining downside with S.T.Json and nullable reference types:
S.T.Json still requires setters on the properties to actually set the values during deserialization. I tried with private ones and it's a no go, so we still can't get an immutable object...
Another option, for those who want to handle missing properties with meaningful exceptions:
using System;
public class Car
{
private string? name;
private int? year;
public string Name
{
get => this.name ?? throw new InvalidOperationException($"{nameof(this.Name)} was not set.");
set => this.name = value;
}
public int Year
{
get => this.year ?? throw new InvalidOperationException($"{nameof(this.Year)} was not set.");
set => this.year = value;
}
}
I am sending a Json Array from the client web application to asp.net webapi.
For example,
{
"SurveyId":3423,
"CreatorId":4235,
"GlobalAppId":34,
"AssociateList":[
{"AssociateId":4234},
{"AssociateId":43},
{"AssociateId":23423},
{"AssociateId":432}
],
"IsModelDirty":false,
"SaveMode":null
}
Here Associate List is a JSON Array,
Usually it will automatically serialize to a List<> object.
Using the below code ,i am posting the response to the WebApi
public IEnumerable<Associate> Post(ResponseStatus responseStatus)
{
return this.responsestatusrepository.ResponseStatusCheck(responseStatus);
}
The ResponseStatus class is shown below.
public class ResponseStatus : AppBaseModel
{
public int SurveyId { get; set; }
public int CreatorId { get; set; }
public int GlobalAppId { get; set; }
public List<Associate> AssociateList { get; set; }
}
I have changed the List<> to Collection<> as a part of my code analysis correction.
ie, public Collection<Associate> AssociateList { get; set; }
But it is always getting a null value when we are using collection instead of List. Is there any specific reason for this?
Ok, I think I will have to answer this in an indirect way.
What you are passing on to the server is an array of objects (in JSON format), but once you start processing this in C# the array of objects is now treated as a single c# object. Inside this object, your model expects one of the fields to be a Collection of Associate.
Right, when I work with JSON data similar to whats mentioned in this case - I prefer to use Newtonsofts' JOject.
So here is how I made the C# object with the JSON data you provided:
Used your model:
public class ResponseStatus
{
public int SurveyId { get; set; }
public int CreatorId { get; set; }
public int GlobalAppId { get; set; }
public Collection<Associate> AssociateList { get; set; }
}
public class Associate
{
public int AssociateId { get; set; }
}
Made a routine which takes string (the JSON data), and returns an object of type ResponseStatus:
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
---------------------------------------------------------------------
public static ResponseStatus GetResponseStatusObject(string jsonData)
{
JObject jObject = JObject.Parse(jsonData);
return jObject.ToObject<ResponseStatus>();
}
Now when I call this method and pass on the exact same JSON data which you provided, I get this:
This might not directly solve your problem, but hopefully guide you in the right direction in understanding array/object serialization when working with JavaScript/C#.
Best of luck!
I have an object which I am de-serializing using ToJson<>() method from ServiceStack.Text namespace.
How to omit all the GET only propeties during serialization? Is there any attribute like [Ignore] or something that I can decorate my properties with, so that they can be omitted?
Thanks
ServiceStack's Text serializers follows .NET's DataContract serializer behavior, which means you can ignore data members by using the opt-out [IgnoreDataMember] attribute
public class Poco
{
public int Id { get; set; }
public string Name { get; set; }
[IgnoreDataMember]
public string IsIgnored { get; set; }
}
An opt-in alternative is to decorate every property you want serialized with [DataMember]. The remaining properties aren't serialized, e.g:
[DataContract]
public class Poco
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
public string IsIgnored { get; set; }
}
Finally there's also a non-intrusive option that doesn't require attributes, e.g:
JsConfig<Poco>.ExcludePropertyNames = new [] { "IsIgnored" };
Dynamically specifying properties that should be serialized
ServiceStack's Serializers also supports dynamically controlling serialization by providing conventionally named ShouldSerialize({PropertyName}) methods to indicate whether a property should be serialized or not, e.g:
public class Poco
{
public int Id { get; set; }
public string Name { get; set; }
public string IsIgnored { get; set; }
public bool? ShouldSerialize(string fieldName)
{
return fieldName == "IsIgnored";
}
}
More examples in ConditionalSerializationTests.cs
For nullable members, you also have the ability to set it to null before serializing.
This is particularly useful if you want to create a single view/api model that is re-used for several API calls. The service can touch it up before setting it on the response object.
Example:
public SignInPostResponse Post(SignInPost request)
{
UserAuthentication auth = _userService.SignIn(request.Domain, true, request.Username, request.Password);
// Map domain model ojbect to API model object. These classes are used with several API calls.
var webAuth = Map<WebUserAuthentication>(auth);
// Exmaple: Clear a property that I don't want to return for this API call... for whatever reason.
webAuth.AuthenticationType = null;
var response = new SignInPostResponse { Results = webAuth };
return response;
}
I do wish there was a way to dynamically control the serialization of all members (including non-nullable) on a per endpoint fashion.
I'm trying to learn Entity Framework Code First development with ASP.NET MVC3.
Let's say I have a simple data Model for an Auction and Bids and I'd like to query all the Auctions and their Bids.
I have turned off LazyLoadingEnabled and ProxyCreationEnabled.
Here is the code I have:
public class MiCoreDb2Context : DbContext
{
public MiCoreDb2Context()
: base()
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Auction> Auctions { get; set; }
public DbSet<Bid> Bids { get; set; }
}
public class Auction
{
public int AuctionId { get; set; }
public virtual ICollection<Bid> Bids { get; set; }
}
public class Bid
{
public long BidId { get; set; }
public int AuctionId { get; set; }
[ForeignKeyAttribute("AuctionId")]
public virtual Auction Auction { get; set; }
}
public JsonResult Thing()
{
List<Auction> auctions;
using (var db = new MiCoreDb2Context())
{
var auctions = (from a in db.Auctions.Include("Bids") select a).ToList();
}
return Json(auctions, JsonRequestBehavior.AllowGet);
}
When I load the page, a circular reference occurs. How will I get around this?
When I load the page, a circular reference occurs. How will I get around this?
By using view models (and by the way that's the answer to any question you might have concerning ASP.NET MVC :-)). Ayende Rahien has an excellent series of blog posts on this topic.
Conclusion: absolutely always pass/take view models to/from a view. Absolutely never pass/take models (EF, domain, ...) to/from a view. Once this fundamental rule is being respected you will find out that everything works.
I solved this problem by doing a projection in the Linq to Entities query. This will create anonymous types which can be serialized to json without any circular reference issues.
var result =
from Item in dbContext.SomeEntityCollection
where SomePredicate
select new { Property1 = Item.Property1, Property2 = Item.Property2 };
Return Json(result, JsonRequestBehavior.AllowGet);
BOb