Unexpected Json.NET exception when using MemberSerialization.OptIn and Required.Always - c#

I have declared a model class that has some properties to be populated by JSON and some that will be populated by code, indicated by the JsonObject and JsonProperty attributes. Here is a simplified version:
[JsonObject(MemberSerialization.OptIn, ItemRequired = Required.Always)]
public class AppCard
{
[JsonProperty]
public string Name { get; set; }
[JsonProperty]
public string Author { get; set; }
public bool IsInstalled { get; set; }
}
Here is the JSON from my unit test:
[
{
"name": "App 1",
"author": "Author 1"
},
{
"name": "App 2",
"author": "Author 2"
}
]
And here is where I call DeserializeObject:
appCards = JsonConvert.DeserializeObject<IEnumerable<AppCard>>(content);
Unfortunately, this fails with the following error:
Newtonsoft.Json.JsonSerializationException: Required property 'IsInstalled'
not found in JSON. Path '[0]', line 5, position 3.
Since that property does not have the JsonProperty attribute and OptIn is specified, I assumed the DeserializeObject method would ignore it. Have I misunderstood how these settings are supposed to work together?

This feels like a bug to me. In stepping through the code, Json.Net marks unadorned properties as ignored if the MemberSerialization is set to OptIn, but then does not honor the ignored status when doing the check for required properties. The same thing happens if you use OptOut serialization (the default), mark the object as having required properties and then explicitly mark a property with [JsonIgnore]:
[JsonObject(ItemRequired = Required.Always)]
public class AppCard
{
public string Name { get; set; }
public string Author { get; set; }
[JsonIgnore]
public bool IsInstalled { get; set; }
}
It's possible this was by design, but it seems to violate the "principle of least surprise". You might want to report an issue.
As a workaround, just mark the individual properties as required via the [JsonProperty] attribute instead of setting that option on the class. Since you've already got all your properties marked with [JsonProperty] anyway (by virtue of using OptIn), it should be easy enough to add the Required option with find and replace.
[JsonObject(MemberSerialization.OptIn)]
public class AppCard
{
[JsonProperty(Required = Required.Always)]
public string Name { get; set; }
[JsonProperty(Required = Required.Always)]
public string Author { get; set; }
public bool IsInstalled { get; set; }
}

You are specifying that ItemRequired = Required.Always,
According to the documentation:
Gets or sets a value that indicates whether the object's properties are required.
And your IsInstalled member is effectively a property.
Some options in JsonObject are mutually exclusive though not explicitly stated.

Related

How to completely remove a property from an Object in c#

someone, please tell me the most straightforward syntax to remove a property from a c# object. I don't know why it is not clear on the internet.
{
"id": 1,
"name": "string",
"email": "string",
"cities": []
}
this is the response I get upon calling the get API. I want to remove the cities array, but I don't know why everything is so complicated in c#. I expect a magical short syntax like delete(in JS). Remember that this response is a dbContext response, not a standard object(DTO).
if you going to deserialize json, you can just create a class without cities property
public class Data
{
public int id { get; set; }
public string name { get; set; }
public string email { get; set; }
}
or if you can not change the class properties, just add an ignore attribute
using Newtonsoft.Json;
public class Data
{
.....
[JsonIgnore]
public List<object> cities {get; set;}
}
the code
Data data= JsonConvert.DeserializeObject<Data>(json);
if you want to remove only from json
var jsonParsed = JObject.Parse(json);
jsonParsed.Properties()
.Where(attr => attr.Name == "cities")
.First()
.Remove();
json=jsonParsed.ToString();
first, you can not change the class structure in runtime in C#.
you should define new object with your properties in mind, or use dynamic-expando objects to be able to manipulate object at runtime.
if your issue is only when you want to sterilize your object you can use [JsonIgnore] Property on the model:
public class MyDto
{
public int id { get; set; }
public string name { get; set; }
public string email { get; set; }
[JsonIgnore]
public string[] cities { get; set; }
}
this will tell you serializer to skip that property.
if you want to convert ur already define class to a dynamic object there are lots of ways.
you can use this nugget package that I wrote which have a DeSelect() method that returns a dynamic object without the specified properties:
https://www.nuget.org/packages/linqPlusPlus/1.3.0#readme-body-tab

System.Json - custom rules for property serialization skipping

I am trying to migrate from Newtonsoft.Json to System.Text.Json
However, I ran into a problem since I was using DefaultContractResolver.
My "custom" behaviour have these rules for property serialization:
Skip property serialization if it is marked with ReadOnly attribute
Skip property serialization in case of null (this is supported)
Skip property serialization which would serialize into an empty object
Example:
class Car
{
[ReadOnly]
public string Id { get; set; }
public string Name { get; set; }
public Person Owner { get; set; }
}
class Person
{
[ReadOnly]
public string Id { get; set; }
public string Name { get; set; }
}
Now, imagine, we have this data if no rules would apply.
{
"Id":"1234",
"Name":"Skoda",
"Owner":{
"Id":"abcd",
"Name":null
}
}
Now, if I serialize the object, I would like to get this instead.
{
"Name":"Skoda"
}
In order to ignore individual properties, you need to use the [JsonIgnore] attribute along with one of these conditions:
Always;
Never;
WhenWritingDefault;
WhenWritingNull.
You can also define a default ignore condition through the JsonSerializerOptions type.
If additional behavior is needed, you should write a custom converter.
Example:
class Person
{
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
public string Id { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Name { get; set; }
}
More information:
How to ignore properties with System.Text.Json
How to write custom converters for JSON serialization (marshalling) in .NET

Resharper: Don't change order of member variables in JSON classes

The order of the members can be set up in the ReSharper options in Languages, C#, Type Members Layout. Resharper is doing it correctly. However, I would like to exclude certain classes which contain JSONProperty attribute.
So for example, refer class below. I don't want Resharper to reorder the members in it.
internal class ExecutionParametersJson
{
[JsonProperty("Factor")]
public string Factor { get; set; }
[JsonProperty("Penalty")]
public string Penalty { get; set; }
[JsonProperty("Origin")]
public string Origin { get; set; }
[JsonProperty("InterFactor")]
public string InterFactor { get; set; }
}
I am using latest version of Resharper.
Can anyone show me how to configure Resharper to achieve this?
Actually, I tried putting Order attribute in it. But that didn't do any difference.
[JsonProperty("Factor", Order = 1)]
public string Factor{ get; set; }
However, I would like to exclude certain classes which contain JSONProperty attribute.
Yes that can be done rather easily.
Given this exammple code, note the additional properties I included purely to prove a later point:
internal class ExecutionParametersJson
{
[JsonProperty("Factor")]
public string Factor { get; set; }
public string SomeProperty { get; set; }
[JsonProperty("Penalty")]
public string Penalty { get; set; }
[JsonProperty("Origin")]
public string Origin { get; set; }
public int SomeOtherProperty { get; set; }
[JsonProperty("InterFactor")]
public string InterFactor { get; set; }
}
...then choose Resharper.Options.Code Editing.c#.File Layout, the list of patterns appears:
Choose your preferred pattern. Here I chose Default Pattern. I've been adding to it in the past so it may look different.
Scroll down till you find a region for Properties, you may have to create it like so:
Select Properties, Indexers, ensure Sort By is set to Name.
Double-click Properties, Indexers. The conditions editor appears. Add a top-level And condition; Not and specify the JsonProperty.
Now run Resharper.Edit.Cleanup Code on the file in question. All properties, except those with a JsonProperty attribute, will be sorted alphabetically and placed into a region titled Properties.
internal class ExecutionParametersJson
{
#region Properties
public int SomeOtherProperty { get; set; }
public string SomeProperty { get; set; }
#endregion
[JsonProperty("Factor")]
public string Factor { get; set; }
[JsonProperty("Penalty")]
public string Penalty { get; set; }
[JsonProperty("Origin")]
public string Origin { get; set; }
[JsonProperty("InterFactor")]
public string InterFactor { get; set; }
}
Now the additional properties I included here was just to prove how you format members conditionally. Feel free to remove these properties; the #region or customise to your liking.
Moving on
You may want to tidy this up a bit and create a specific pattern in Resharper called JSON Classes or some such.

How do I serialize (JSON) an object/class but not its children

I have:
public Class City
{
public long ID { get; set }
...
public State State { get; set; }
...
}
public Class State
{
public long ID { get; set; }
...
public Country { get; set; }
...
}
public Class Country
{
public long ID {get; set;}
...
}
in my code I serialize a list<Country> and list<State>, etc...
The problem is if I serialize my list of Cities I get something like this:
{
"ID": 3119841,
"Name": "A Coruna",
...
"State": {
"ID": 3336902,
"Name": "Galicia",
"Country": {
"ID": 2510769,
"Name": "Spain",
...
}
...
}
...
}
this cause memory and performance issues when deserializing due to having thousands of copies of the same country object and state object, etc... what I would like to generate is:
{
"ID": 3119841,
"Name": "A Coruna",
...
"StateID": 3336902,
...
}
and then I will link up the city to the states (which I will have linked up to the country, etc.)
If I use the Ignore flag (JsonIgnoreAttribute) on the fields in the state and country classes, then I will not be able to Serialize them (I think)...
How do I achieve what I am after? (I am currently using Json.NET, but am happy to use anything that will accomplish the goal.)
To exclude a child structure from being serialized, you can use the [JsonIgnore] attribute:
public class City
{
public long ID { get; set }
...
[JsonIgnore]
public State State { get; set; }
...
}
If you want to flatten the structure, things become more complex. Your options are:
Create an intermediate flattened structure, as the result of e.g. a Linq projection.
Create a custom contract resolver for your structure, an example that may provide some inspiration on how to achieve this can be found in this question: How to flatten a referenced object into two json.net properties on the referer?
Expose the additional properties of your State class that you want to "flatten" in the City class as properties:
public long StateID { get { return State.ID; } }
Note that some frameworks will also require you to add the [ScriptIgnore] and/or [NonSerialized] attributes, in addition to or as alternatives of the [JsonIgnore].
You add [NonSerialized] attribute on top of that field.
To have a plain object (no childs), you can create a model for that object and map the required properties.
public class City
{
public long ID { get; set }
...
[NonSerialized()]
public State State { get; set; }
...
}
To have a plain object (no childs), you can create a model for that object and map required properties (only the properties you need).
public class CityModel
{
public long ID { get; set }
public string Name { get; set; }
...
public long StateID { get; set; }
...
}
Now we map the required properties. (this can be in a function that you can use when you want)
var cityModel = new CityModel {
ID = city.ID,
Name = city.Name,
...
StateID = city.State.ID
...
}
You can ignore the State property using JsonIgnore and have an additional property called StateID which gets its value from the State object.
public Class City
{
public long ID { get; set }
...
public int StateID
{
get
{
return State.ID;
}
}
[JsonIgnore]
public State State { get; set; }
...
}
I believe your most appropriate solution is follows; first, add all the required attributes to refuse serialization on your State object (which set you use totally depends on what serailizer you use):
JsonIgnore: http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonIgnoreAttribute.htm
ScriptIgnore: https://msdn.microsoft.com/en-us/library/system.web.script.serialization.scriptignoreattribute%28v=vs.110%29.aspx
NonSerialized (fields only): https://msdn.microsoft.com/en-us/library/system.nonserializedattribute%28v=vs.110%29.aspx
After doing so, I recommend you add a property that can manipulate the state object by ID:
public class City
{
public long ID { get; set; }
...
[JsonIgnore, ScriptIgnore]
public State State { get; set; }
public long StateID
{
get { return State.ID; }
/* Optionally include a set; some JSON serializers may complain in
absence of it, up to you to experiment with */
set { State = new State(value); }
}
...
}
This completely preserves your structure, and a well-designed constructor in State can handle accepting an Id and building a State object from it. If you can't build a State object from Id alone, and your JSON Serializer requires a set mutator on it, then you could simply empty the body of it.
You can expand this to your other objects as well:
public class State
{
public long ID { get; set; }
...
[JsonIgnore, ScriptIgnore]
public Country Country { get; set; }
public long CountryId
{
get { return Country.Id; }
set { Country = new Country(value); }
}
...
}
The major advantage to this model, is that if you do it correctly and design State and Country appropriately, your JSON Serializer will do all the heavy lifting.
Also, as Alex mentioned, you may also create Contract Resolvers, though this is extra work.
This all really depends on your data set. When dealing with a large dataset, I simply use the [JsonIgnore] flag on the property. From there, I would request the cities and states async. I would put the states in a IEnumerable, and then use a LINQ query to populate the IEnumerable of cities.
Something like this:
Cities.ToList().ForEach((c) =>
{
c.State= States.FirstOrDefault(x => x.Id == c.StateId);
});
Also, I tend to use virtual properties to reference other objects like State.
Something like this:
public class City
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public int StateId { get; set; }
[JsonIgnore]
[ForeignKey("StateId")]
public virtual State State { get; set; }
}
public class State
{
public int Id { get; set; }
public string Name { get; set; }
}
If I have a localdb at my disposal, then I would use a different solution.
If it's a small enough dataset, I just serialize the whole object and let JSon.Net do the heavy lifting.
I'm pretty sure you cannot use [NonSerialized] with a custom class property. At least, it threw an error on my-side.
You can use [ScriptIgnore]:
public Class City
{
public long ID { get; set }
...
[ScriptIgnore]
public State State { get; set; }
...
}

Why would a WCF DataContract not serialize members in alphabetical order?

I have several DataContracts that looks similar to this (shortened for brevity):
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
[DataMember(IsRequired = true)]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember(IsRequired = true)]
public int Id { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
I hadn't taken notice of the serialized messages before but after a recent change, two things were done: I added a new property, called ReturnCode, and ran CodeMaid's "Reorganize", which alphabetized the properties.
It now looked something like this:
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
public ExtensionDataObject ExtensionData { get; set; }
[DataMember(IsRequired = true)]
public int Id { get; set; }
[DataMember(IsRequired = true)]
public string Name { get; set; }
[DataMember]
public int ReturnCode { get; set; }
[DataMember]
public string Value { get; set; }
}
According to Microsoft's page on Data Contract Member Order I realized ReturnCode would break the contract since the serializer would insert it before Value, so I added an Order attribute value, assuming the original order was alphabetic, yielding:
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
public ExtensionDataObject ExtensionData { get; set; }
[DataMember(IsRequired = true, Order = 0)]
public int Id { get; set; }
[DataMember(IsRequired = true, Order = 1)]
public string Name { get; set; }
[DataMember(Order = 3)]
public int ReturnCode { get; set; }
[DataMember(Order = 2)]
public string Value { get; set; }
}
This however threw an exception that the deserialized members were out of order. I rolled back to a prior changeset, before all the changes, and sure enough the original order of the members was not alphabetic in the SOAP request (viewed through Fiddler), but following the original order expressed in the code, ie: Name, Value, Id.
I'm currently in the process of adding Order values to all my old DTO types to sequence them according to their prior, pre-alphabetizing of the properties, arrangement. What I'd like to know is why the coded order instead of alphabetized order was being used by the serializer? Microsoft's rules say:
Next in order are the current type’s data members that do not have the
Order property of the DataMemberAttribute attribute set, in
alphabetical order.
Update:
After I added the Order values to sequence the properties in their original order, I again ran Fiddler and it's still using the order the items are literally coded in. In other words, for some reason, my WCF service is completely ignoring any serialization sequencing logic and just sequencing the properties by the order they appear in the .cs file. In fact, the only way I was able to get it to serialize properly was to physically rearrange the properties in each type to their original order. That worked, but it's not preferred.
Update 2 - Solution:
Following Dracor's advice, I added [XmlElement(Order = 1)] attributes and an XmlRootAttribute to my DTOs. The SOAP serialization DID end up following the ordering assigned by these attributes. I hadn't considered it but my service does use Castle DynamicProxy and so I'm guessing it's changing the serializer from DataContractSerializer to XmlSerializer.
Why don't you simply use XmlSerializer to Serialize/Deserialize your XML? It's way more forgiving than DataContractSerializer, and works most of the time.

Categories