AddNewtonsoftJson is not overriding System.Text.Json - c#

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)

Related

How to Serialize and Deserialize a giant Trie? [duplicate]

I recently upgraded a solution to be all .NET Core 3 and I have a class that requires the class variables to be fields. This is a problem since the new System.Text.Json.JsonSerializer doesn't support serializing nor deserializing fields but only handles properties instead.
Is there any way to ensure that the two final classes in the example below have the same exact values?
using System.Text.Json;
public class Car
{
public int Year { get; set; } // does serialize correctly
public string Model; // doesn't serialize correctly
}
static void Problem() {
Car car = new Car()
{
Model = "Fit",
Year = 2008,
};
string json = JsonSerializer.Serialize(car); // {"Year":2008}
Car carDeserialized = JsonSerializer.Deserialize<Car>(json);
Console.WriteLine(carDeserialized.Model); // null!
}
In .NET Core 3.x, System.Text.Json does not serialize fields. From the docs:
Fields are not supported in System.Text.Json in .NET Core 3.1. Custom converters can provide this functionality.
In .NET 5 and later, public fields can be serialized by setting JsonSerializerOptions.IncludeFields to true or by marking the field to serialize with [JsonInclude]:
using System.Text.Json;
static void Main()
{
var car = new Car { Model = "Fit", Year = 2008 };
// Enable support
var options = new JsonSerializerOptions { IncludeFields = true };
// Pass "options"
var json = JsonSerializer.Serialize(car, options);
// Pass "options"
var carDeserialized = JsonSerializer.Deserialize<Car>(json, options);
Console.WriteLine(carDeserialized.Model); // Writes "Fit"
}
public class Car
{
public int Year { get; set; }
public string Model;
}
For details see:
How to serialize and deserialize (marshal and unmarshal) JSON in .NET: Include fields.
Issues #34558 and #876.
If you want this for all MvcControllers in API project you can do similar to this in setup:
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.IncludeFields = true;
});
Please try this library I wrote as an extension to System.Text.Json to offer missing features: https://github.com/dahomey-technologies/Dahomey.Json.
You will find support for fields.
using System.Text.Json;
using Dahomey.Json
public class Car
{
public int Year { get; set; } // does serialize correctly
public string Model; // will serialize correctly
}
static void Problem() {
JsonSerializerOptions options = new JsonSerializerOptions();
options.SetupExtensions(); // extension method to setup Dahomey.Json extensions
Car car = new Car()
{
Model = "Fit",
Year = 2008,
};
string json = JsonSerializer.Serialize(car, options); // {"Year":2008,"Model":"Fit"}
Car carDeserialized = JsonSerializer.Deserialize<Car>(json);
Console.WriteLine(carDeserialized.Model); // Fit
}

C# JSON.NET How to ignore a property in deserialization but not in serialization

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 to use class fields with System.Text.Json.JsonSerializer?

I recently upgraded a solution to be all .NET Core 3 and I have a class that requires the class variables to be fields. This is a problem since the new System.Text.Json.JsonSerializer doesn't support serializing nor deserializing fields but only handles properties instead.
Is there any way to ensure that the two final classes in the example below have the same exact values?
using System.Text.Json;
public class Car
{
public int Year { get; set; } // does serialize correctly
public string Model; // doesn't serialize correctly
}
static void Problem() {
Car car = new Car()
{
Model = "Fit",
Year = 2008,
};
string json = JsonSerializer.Serialize(car); // {"Year":2008}
Car carDeserialized = JsonSerializer.Deserialize<Car>(json);
Console.WriteLine(carDeserialized.Model); // null!
}
In .NET Core 3.x, System.Text.Json does not serialize fields. From the docs:
Fields are not supported in System.Text.Json in .NET Core 3.1. Custom converters can provide this functionality.
In .NET 5 and later, public fields can be serialized by setting JsonSerializerOptions.IncludeFields to true or by marking the field to serialize with [JsonInclude]:
using System.Text.Json;
static void Main()
{
var car = new Car { Model = "Fit", Year = 2008 };
// Enable support
var options = new JsonSerializerOptions { IncludeFields = true };
// Pass "options"
var json = JsonSerializer.Serialize(car, options);
// Pass "options"
var carDeserialized = JsonSerializer.Deserialize<Car>(json, options);
Console.WriteLine(carDeserialized.Model); // Writes "Fit"
}
public class Car
{
public int Year { get; set; }
public string Model;
}
For details see:
How to serialize and deserialize (marshal and unmarshal) JSON in .NET: Include fields.
Issues #34558 and #876.
If you want this for all MvcControllers in API project you can do similar to this in setup:
builder.Services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.IncludeFields = true;
});
Please try this library I wrote as an extension to System.Text.Json to offer missing features: https://github.com/dahomey-technologies/Dahomey.Json.
You will find support for fields.
using System.Text.Json;
using Dahomey.Json
public class Car
{
public int Year { get; set; } // does serialize correctly
public string Model; // will serialize correctly
}
static void Problem() {
JsonSerializerOptions options = new JsonSerializerOptions();
options.SetupExtensions(); // extension method to setup Dahomey.Json extensions
Car car = new Car()
{
Model = "Fit",
Year = 2008,
};
string json = JsonSerializer.Serialize(car, options); // {"Year":2008,"Model":"Fit"}
Car carDeserialized = JsonSerializer.Deserialize<Car>(json);
Console.WriteLine(carDeserialized.Model); // Fit
}

C# DataContractJsonSerializer ReadObject not throwing error if member is missing

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; }

Can not deserialize JSON containing $ref keys

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.

Categories

Resources