I have a user class like
public class User
{
public string UserName {get;set;}
public string Application {get;set;}
}
Now, I am using it like
var jsonSerializer = new DataContractJsonSerializer(typeof(User));
var objApp = (User)jsonSerializer.ReadObject(new MemoryStream(Encoding.Unicode.GetBytes(JsonInput)));
But my JSON JsonInput doesn't contain both values for example Application is not available in the JSON. This still serializes with on only UserName.
The JSON and class above is an example, there are quite a few members for me to check them individually !
I want to make sure, the JSON contains all the members of the class, if not , throw error.
But I can't seem to find way. What am I missing here?
If you use Newtonsoft (which you can install with Install-Package Newtonsoft.Json) - You can set the MissingMemberHandling property of the settings;
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;
Then pass it;
var userObj = JsonConvert.DeserializeObject<User>(jsonInput, settings);
If you must use Datacontract instead, you can decorate your User object with required attributes;
[DataMember(IsRequired=true)]
public string? RequiredProperty { get; set; }
Related
I am have requirement about to show property in response base on the class property value.If we are initializing the class by default all the properties all initializing. so, I want to ignore the properties based on the property.
Here is my class
[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
Public class Student
{
public string name{get; set;}
public string address{get;set;}
public bool success{get;set;}
}
In the above class, Based on the value of success.I have to decide to show the properties in response even if they are null
I thought one solution was keeping
[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
but dynamically i have to set the value of ItemNullValueHandling i didn't know that how to set dynamically can any one please suggest me.
Thanks in Advance!
You wont be able to dynamically change the serialisation settings using attributes as they are fixed at compile time.
However, you can pass an instance of JsonSerializerSettings to the Deserialize() method as shown below.
var showAllProperties = student.success;
var serializerSettings = new JsonSerializerSettings {
NullValueHandling = showAllProperties ? NullValueHandling.Include : NullValueHandling.Ignore;
}
var result = JsonConvert.SerializeObject(student, serializerSettings)
I have an object I am serializing into JSON then deserializing back.
The structure of one of the properties has changed and now deserializing crashes, so I need to ignore deserializing that property for now.
I can ignore the property completely with [JsonIgnore, JsonProperty(Required = Required.Default)], but that also ignores the property from serialization - which needs to stay so no data is lost, even if it isn't' being serialized at this moment.
There is an answer here, although it's a bit old: https://stackoverflow.com/a/31732029/12431728 However, it still seems viable to me, I'm not aware of a better / different way to do it. That answer suggests marking the real property with JsonIgnore and creating a "get-only proxy property."
Then it goes on to suggest creating a custom ContractResolver if you need this functionality for many properties (AKA reusable solution).
You could use a JsonContractResolver to set the JsonProperty.ShouldDeserialize property as seen in one of the test suites of Newtonsoft.Json.
For Example,
public class ShouldDeserializeContractResolver : DefaultContractResolver
{
public static new readonly ShouldDeserializeContractResolver Instance = new ShouldDeserializeContractResolver();
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
MethodInfo shouldDeserializeMethodInfo = member.DeclaringType.GetMethod("ShouldDeserialize" + member.Name);
if (shouldDeserializeMethodInfo != null)
{
property.ShouldDeserialize = o => { return (bool)shouldDeserializeMethodInfo.Invoke(o, null); };
}
return property;
}
}
Example Code
var instance = new RootObject { ID = 2, DisplayName = "John Doe" };
var json = JsonConvert.SerializeObject(instance);
var settings = new JsonSerializerSettings
{
ContractResolver = ShouldDeserializeContractResolver.Instance
};
Console.WriteLine(json);
var deserializedInstance = JsonConvert.DeserializeObject<RootObject>(json, settings);
Console.WriteLine($"Deserialized => Id={deserializedInstance.ID}, Name={deserializedInstance.DisplayName} ");
Where RootObject is defined as
public class RootObject
{
public int ID { get; set; }
public string DisplayName { get; set; }
public bool ShouldDeserializeDisplayName() => false;
}
Output
{"ID":2,"DisplayName":"John Doe"} //During Serialization
Deserialized => Id=2, Name= // During Deserialization
You could serialise and deserialise from a JObject as in the link from Amadeu Antunes, although this solution is rather inelegant, as suddenly you've thrown loose typing into the mix.
Another potentially easier possibility is that if you have access to all of the json objects that you are deserialising from, you could do a bulk update in notepad++, ssms or whatever and just add in some default value into the json files.
How do I use the (generated) JsonProperty Name = name from JSON file to access a single element in a corresponding C# Class?
Here is what I am trying to do …
I have a Xamarin “classic” Newtonsoft application where I load a JSON file into a C# class MagicTime.
Here is the first part of my class - This was generated automatically by the newtonsoft web site using my JSON file
public partial class MagicTime
{
[JsonProperty("Accel Delay")]
public long AccelDelay { get; set; }
[JsonProperty("Accel Period")]
public long AccelPeriod { get; set; }
[JsonProperty("Alt DT Step")]
public long AltDtStep { get; set; }
[JsonProperty("Beep On Command")]
public bool BeepOnCommand { get; set; }
[JsonProperty("Bunt Step")]
public long BuntStep { get; set; }
[JsonProperty("Bunt Timeout")]
This is how load/deserialize an instant where loadp is string variable containing the contents of the JSON file
MagicTime MT = JsonConvert.DeserializeObject<MagicTime>(loadp );
This works fine
In the application I modify the values of some data element e.g
Looking at the above – MT.AccelDelay = 21;
I then reverse the process and write /Serialize I out
That work too.
Now I have an new requirement to use the JsonProperty name to access the corresponding C# data item
It the example above I want to use [JsonProperty("Accel Delay")] to access the corresponding c# element MT.AccelDelay.
I have seen examples where a JSON string is loaded into JObject to do this but not my case where is no (that I can see) JObject
You can use Newtonsoft's ContractResolver for this purpose, as it defines how Json.NET maps from c# objects to JSON objects.
First, define the following extension method:
public static partial class JsonExtensions
{
static readonly IContractResolver defaultResolver = new JsonSerializer().ContractResolver;
public static T GetJsonPropertyValue<T>(object obj, string propertyName, IContractResolver resolver = null)
{
resolver = resolver ?? defaultResolver;
var contract = resolver.ResolveContract(obj.GetType());
if (contract is JsonObjectContract objectContract)
{
var property = objectContract.Properties.GetClosestMatchProperty(propertyName);
if (property == null)
throw new JsonException(string.Format("Unknown property {0}", propertyName));
return (T)property.ValueProvider.GetValue(obj);
}
throw new JsonException(string.Format("Invalid contract {0}", contract));
}
}
And now you can do:
var accelDelay = JsonExtensions.GetJsonPropertyValue<long>(MT, "Accel Delay");
If you don't know the type in advance, you can just do:
var accelDelay = JsonExtensions.GetJsonPropertyValue<object>(MT, "Accel Delay");
And if you are using camel casing, do:
var resolver = new CamelCasePropertyNamesContractResolver();
var accelDelay = JsonExtensions.GetJsonPropertyValue<object>(MT, "Accel Delay", resolver);
Notes:
If the incoming obj maps to something other than a JSON object (e.g. an array or primitive) then GetJsonPropertyValue() will throw an exception. It will also throw if the property is set-only.
If you need a way to discover all the JSON property names for a given type, see Get a list of JSON property names from a class to use in a query string, especially this answer.
Demo fiddle here.
I have upgraded my version of .Net Core from preview 2 to preview 6 which has broken a couple of things. Most significant is that I cannot use newtonsoft JSON anymore.
AddNewtonsoftJson in ConfigureServices seemingly does nothing, and the new Json serializer seems to work on properties only, not fields. It does not see the JSONIgnoreAttribute.
In ConfigureServices (in Startup) I have the line
services.AddMvc(x => x.EnableEndpointRouting = false).AddNewtonsoftJson();
which doesn't seem to be doing what it should. In my application, only properties are serialized, not fields, and the [JSONIgnore] attribute does nothing.
The lack of fields I can work around by promoting all the public fields I need to be properties, but I need to be able to ignore some.
Has anyone else had this? How do I either get the new JSON serializer to ignore some properties and serialize public fields, or go back to Newtonsoft?
System.Text.Json has a JsonIgnore attribute, please see How to ignore properties with System.Text.Json.
In order for it to work you will need to remove the dependency on Newtonsoft.Json and change the namespaces in relevant files to System.Text.Json.Serialization;
Sytem.Text.Json can include fields, but only public ones.
using System.Text.Json;
using System.Text.Json.Serialization;
var json = JsonSerializer.Serialize(new O(), new JsonSerializerOptions() { WriteIndented = true});
Console.WriteLine(json);
class O {
[JsonInclude]
public int publicField = 1;
//[JsonInclude]
//This won't work and throws an exception
//'The non-public property 'privateField' on type 'O' is annotated with 'JsonIncludeAttribute' which is invalid.'
private int privateField = 2;
[JsonIgnore]
public int P1 { get; set;} = 3;
public int P2 { get; set; } = 4;
}
This results in:
{
"P2": 4,
"publicField": 1
}
Alternatively you can use IncludeFields
var json = JsonSerializer.Serialize(new O(), new JsonSerializerOptions() { IncludeFields = true});
(Reference: Include fields)
I have the following code trying to deserialize a JSON string and the library gives me this error:
Additional content found in JSON reference object. A JSON reference object should only have a $ref property. Path 'user.obj', line 1, position 34.
Any idea what is wrong? (I understand that it is complaining about the second $ref, but I don't know why.) What is the workaround ?
void Main()
{
var s = "{\"user\": {\"$ref\": \"123456\", \"obj\": {\"$ref\": \"123456\"}}}";
JsonConvert.DeserializeObject<Root>(s).Dump();
}
// Define other methods and classes here
public class Root
{
[JsonProperty("user")]
public User User { get; set; }
}
public class User
{
[JsonPropertyAttribute("$ref")]
public string Ref { get; set; }
[JsonPropertyAttribute("obj")]
public Obj Obj { get; set; }
}
public class Obj
{
[JsonPropertyAttribute("$ref")]
public string Ref { get; set; }
}
Json.Net uses $ref along with $id as metadata to preserve object references in JSON. So when it sees $ref it assumes that property is not part of the actual JSON property set, but an internal identifier referring to a matching $id somewhere else in the JSON. Since your usage of $ref is different than what Json.Net expects to see, it is throwing an error.
UPDATE
In Json.Net version 6.0.4 and later, there is now a setting by which you can instruct the deserializer to treat these metadata properties as normal properties instead of consuming them. All you need to do is set the MetadataPropertyHandling setting to Ignore and then deserialize as usual.
var settings = new JsonSerializerSettings();
settings.MetadataPropertyHandling = MetadataPropertyHandling.Ignore;
var obj = JsonConvert.DeserializeObject<FormDefinitionList>(json, settings);
Prior to version 6.0.4, a workaround was needed to solve this issue. If you cannot upgrade to the lastest version of Json.Net, see my answer to a similar question for some possible solutions.