I am tiring to serialize a fairly large list of entities, that are all derived from a base class.
I only need the base class properties in the client. How do I achieve this without instantiating a new instance of the base class?
I have tried creating a custom ContractResolver, but it seems that it does a getType() at runtime, instead of using the Type of the list/Array being serialized
See code sample below.
I want to achieve. castBaseString == actualBaseString ;
So I want castBaseString to = [{"Id":1},{"Id":2}] not [{"Value":"value","Id":1},{"Value":"value2","Id":2}]
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Tests {
[TestClass]
public class JsonNetTest {
class Base {
public int Id { get; set; }
}
class Derived : Base {
public string Value { get; set; }
}
class OtherDerived : Base {
public string Value { get; set; }
public string OtherValue { get; set; }
}
[TestMethod]
public void Test() {
IEnumerable<Derived> deriveds = new Derived[] {
new Derived {Id = 1, Value = "value" },
new Derived {Id = 2, Value = "value2" }
};
IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();
IEnumerable<Base> bases = new Base[] {
new Base {Id = 1 },
new Base {Id = 2 }
};
JsonSerializerSettings s = new JsonSerializerSettings();
var derString = JsonConvert.SerializeObject(deriveds, s);
var castBaseString = JsonConvert.SerializeObject(castBases, s);
var actualBaseString = JsonConvert.SerializeObject(bases, s);
Assert.AreEqual(actualBaseString, castBaseString);
Assert.AreNotEqual(castBaseString, derString);
}
}
}
EDIT BASED ON COMMENTS
Additional Context:
I just posted this simple test case for clarity.
the actual context this is being used is in an aspnet core application.
Consider there are 3 controllers
/api/DerivedController/
/api/OtherDerivedController/
/api/BaseController/
when a client calls 1, we want to return a list of Derived, when a client calls
2 we want to return a list of OtherDerived when they call 3, we want to return a list of Base
The data is stored in 2 different tables in the database TBL_DERIVED and TBL_OTHERDERIVED.
What we want to achieve when they call base is to return data from One or both of these tables, but just the common properties of these tables.
Hope this clarifies.
If you don't want to use attributes, you can use a ContractResolver to force only properties from Base to be serialized:
public class DerivedTypeFilterContractResolver<T> : DefaultContractResolver {
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (property.DeclaringType != typeof(T)) {
property.ShouldSerialize = instance => false;
}
return property;
}
}
Then use it like this:
void Main() {
IEnumerable<Derived> deriveds = new Derived[] {
new Derived {Id = 1, Value = "value" },
new Derived {Id = 2, Value = "value2" }
};
IEnumerable<Base> castBases = deriveds.Cast<Base>().ToList();
IEnumerable<Base> bases = new Base[] {
new Base {Id = 1 },
new Base {Id = 2 }
};
JsonSerializerSettings s = new JsonSerializerSettings {
ContractResolver = new DerivedTypeFilterContractResolver<Base>()
};
var derString = JsonConvert.SerializeObject(deriveds, s);
var castBaseString = JsonConvert.SerializeObject(castBases, s);
var actualBaseString = JsonConvert.SerializeObject(bases, s);
Console.WriteLine(castBaseString);
}
class Base {
public int Id { get; set; }
}
class Derived : Base {
public string Value { get; set; }
}
Output:
[{"Id":1},{"Id":2}]
Add [JsonIgnore] top of property.
class Derived : Base {
[JsonIgnore]
public string Value { get; set; }
}
Turns out this is not possible the way the json serialization works.
#stuartd answer might be a good workaround if you only have limited cases you want to do this for.
Related
This question already has answers here:
Elegant initialization of an array of class instances in C# [closed]
(3 answers)
Constructor for an array of class objects
(2 answers)
Closed 1 year ago.
Hi I have this code not sure how I can set the value of Value and Value1
public class testclass {
public ID[] test { get; set; }
public class ID
{
[JsonPropertyName("ID")]
public string Value { get; set; }
public string Value1 { get; set; }
}
}
e.g. var a = new testclass ();
a.test = ????
The fastest way with your existing code is:
a.test = new testclass.ID[] { new testclass.ID() { Value = "test", Value1 = "test1" } };
But some refactoring might help you a bit. Here's a fully compiling example.
Constructors were added to make the syntax cleaner. Rather than using the syntax of new ID { Value = "", Value1 = "" }; you can use new ID("", "");. Note that since you're doing serialization/deserialization, you may need to explicitly include a parameterless constructor, as it's unclear from your small code sample how this might get handled.
And the ID class was separated out, no longer 'underneath' the testclass class.
using System.Text.Json.Serialization; // from System.Text.Json library
namespace SomeNamespace
{
public class Program
{
static void Main()
{
var id = new ID("hello", "world");
var a = new testclass(id);
}
}
public class testclass
{
public ID[] test { get; set; }
public testclass() { /* might need parameterless for serialization */ }
// just makes an array with one entry
public testclass(ID id)
{
test = new ID[] { id };
}
}
// separate ID class from 'testclass'
public class ID
{
[JsonPropertyName("ID")]
public string Value { get; set; }
public string Value1 { get; set; }
public ID() { /* might need parameterless for serialization */ }
public ID(string value, string value1)
{
Value = value;
Value1 = value1;
}
}
}
This is what you're looking for:
var a = new testclass ();
a.test = new testclass.ID[]{};
You can also use object initialization like this:
a.test = new testclass.ID[]{
new testclass.ID(){Value = "1", Value1 = "2"},
new testclass.ID(){Value = "AnotherValue", Value1 = "10"}
};
And to read individual values:
a.test[0].Value == "1"
a.test[1].Value1 == "10"
Though these kind of nested classes are not recommended for most cases, it should work.
I need to deserialize a JSON string into a type which is not know at compile time. There are several classes that it can be deserialized into. The name of the class is provided as input into the application and based on that I want to instantiate the class (already done this through reflection):
var type = Type.GetType(className);
var myClassInstance = (IParser)Activator.CreateInstance(type);
...and then use its type as the generic type parameter for JsonConvert.DeserializeObject<typeof(myClassInstance).Name>(jsonString) but that doesn't work.
How can I provide the class to DeserializeObject<>() dynamically?
Instead of using an generic method overload like JsonConvert.DeserializeObject<T>(String) and having to resort to reflection as some comments state, you could simply use the non generic counterpart JsonConvert.DeserializeObject(String, Type), which just takes in a Type instance like you already have!
Implementation
Initialization
var class1s = new Class1() {
ID = 1, Name = "Test", Comment = "This Code is Tested!."
};
var class2s = new Class2() {
xVal1 = 1, XVal2 = 5, xval3 = 10
};
var JSON1 = Newtonsoft.Json.JsonConvert.SerializeObject(class1s);
var JSON2 = Newtonsoft.Json.JsonConvert.SerializeObject(class2s);
Calling Functions
var classname1 = typeof(Class1).FullName;
var type1 = Type.GetType(classname1);
var classname2 = typeof(Class2).FullName;
var type2 = Type.GetType(classname2);
var c = LocalConverter(JSON1, type1);
var c2 = LocalConverter(JSON2, type2);
Class Models
public class Class1 {
public int ID {
get;
set;
}
public string Name {
get;
set;
}
public string Comment {
get;
set;
}
}
public class Class2 {
public int xVal1 {
get;
set;
}
public int XVal2 {
get;
set;
}
public int xval3 {
get;
set;
}
}
Required Method
private object LocalConverter(string o, Type xtype) {
return Newtonsoft.Json.JsonConvert.DeserializeObject(o, xtype);
}
This question already has answers here:
How to ignore JsonProperty(PropertyName = "someName") when serializing json?
(2 answers)
Closed 2 years ago.
I have a class in my project which needs to be serialized for two different use cases.
As I can't add two diffrent attributes to on property I would like to serialize the objects of the class
one time with the [JsonProperty("attributname")] decleration and one time with the property name it self.
For ex:
public class Contact
{
[JsonProperty("name")]
public string Lastname { get; set; }
}
public class Program
{
public void SerializeByJsonPropertyName()
{
var contact = new Contact()
{
Lastname = "Harber"
}
var requestJson = JsonConvert.SerializeObject(contact);
// Serialized Object requestJson:
// {
// "name" = "Harber"
// }
}
public void SerializeByPropertyName()
{
var contact = new Contact()
{
Lastname = "Harber"
}
var requestJson = JsonConvert.SerializeObject(contact /*, ? Setting ?*/);
// Serialized Object requestJson:
// {
// "Lastname" = "Harber"
// }
}
}
The first szenario works totaly fine, but for the second szenario I couldĀ“t find any solution. Except creating two classes or duplicate the properties in my class.. IS there any setting in Newtonsofts JsonConverter for doing this?
Thanks for your help!
You can create different naming strategies, then create different settings, each setting has a Contract resolver, each contract resolver has a naming strategy, then you supply for each settings the naming strategy you want to use,
something like this
public static class JsonSerializingSettings {
public static JsonSerializerSettings JsonUnModified{ get; set; } = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver() {
NamingStrategy = new UnmodifiedNamingStrategy()
}
};
public static JsonSerializerSettings JsonDefault { get; set; } = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver() {
NamingStrategy = new DefaultNamingStrategy()
}
};
}
public class UnmodifiedNamingStrategy : NamingStrategy {
protected override string ResolvePropertyName(string name) {
return name;
}
}
and when you want to use it
var requestJson = JsonConvert.SerializeObject(contact,JsonSerializingSettings.JsonDefault);
// Serialized Object requestJson:
// {
// "name" = "Harber"
// }
var requestJson = JsonConvert.SerializeObject(contact,JsonSerializingSettings.JsonUnModified);
// Serialized Object requestJson:
// {
// "Lastname" = "Harber"
// }
I am using a public library that exposes a model for SwaggerDocument. It comes with some serialization logic added via annotations to specify what should be ignored during serialization, and what order should be applied during serialization and deserialization:
[Newtonsoft.Json.JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = 6, PropertyName = "basePath")]
public string BasePath;
I want to change these annotations, without having to creating my own class with all the other logic copied over. Can I extend this class and override the annotations? E.g.
MySwaggerDocument: SwaggerDocument
{
#override
[Newtonsoft.Json.JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate, Order = 4, PropertyName = "basePath")]
public string BasePath;
}
It is not an ideal solution, although this works.
You could use the following strategy to expose some of the attributes of the base class which are messing up the order in your custom derived class.
The drawback is to declare some of the base class' attributes, but as you can see, the logic behind this is quite simple (the get/set syntax is C# 7.0).
using Newtonsoft.Json;
using System;
namespace JsonTest
{
public class Base
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = 1, PropertyName = "A")]
public string A { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = 2, PropertyName = "X")]
public string X { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = 3, PropertyName = "B")]
public string B { get; set; }
}
public class Derived : Base
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = 4, PropertyName = "C")]
public string C { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = 5, PropertyName = "X")]
public new string X
{
get => base.X;
set => base.X = value;
}
[JsonProperty(DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate, Order = 6, PropertyName = "D")]
public string D { get; set; }
}
class Program
{
static void Main(string[] args)
{
Base b = new Base() { A = "a", B = "b", X = "x" };
string serB = JsonConvert.SerializeObject(b);
Console.WriteLine($"Serialized base class:\r\n {serB}");
Derived d = new Derived() { A = "a", B = "b", C = "c", D = "d", X = "x" };
string serD = JsonConvert.SerializeObject(d);
Console.WriteLine($"Serialized derived class:\r\n {serD}");
}
}
}
Output:
Serialized base class:
{"A":"a","X":"x","B":"b"}
Serialized derived class:
{"A":"a","B":"b","C":"c","X":"x","D":"d"}
I have this structure.
public class FirstClass
{
public List<Foo> FooList{ get; set; }
}
public class Foo{
//Ex:
//public string Name{ get; set; }
}
public List<Foo> GetFoo(){
//I'm use Firstclass like this here typeof(FirstClass);
//I want create here dynamic property for Foo class.
}
And my problem is, i want create property for "Foo" class from "GetFoo()" function. Same time, this function return "List" "Foo" type. I'm research "Dynamically Add C# Properties at Runtime", "How to dynamically create a class in C#?" but the answers in these links are not referenced as return values or referenced to another class. How i can do this?
You can dynamically create classes, which inherits Foo, with any additional properties. Thus you can add instances of those dynamic classes into List<Foo>.
To do so, one can generate a code string like following:
var bar1Code = #"
public class Bar1 : Foo
{
public Bar1(int value)
{
NewProperty = value;
}
public int NewProperty {get; set; }
}
";
Then compile it using CSharpCodeProvider:
var compilerResults = new CSharpCodeProvider()
.CompileAssemblyFromSource(
new CompilerParameters
{
GenerateInMemory = true,
ReferencedAssemblies =
{
"System.dll",
Assembly.GetExecutingAssembly().Location
}
},
bar1Code);
Then one can create an instance of Bar1, add it to List<Foo> and, e.g. cast it to dynamic to access the dynamic property:
var bar1Type = compilerResults.CompiledAssembly.GetType("Bar1");
var bar2Type = compilerResults.CompiledAssembly.GetType("Bar2"); // By analogy
var firstClass = new FirstClass
{
FooList = new List<Foo>
{
(Foo)Activator.CreateInstance(bar1Type, 56),
(Foo)Activator.CreateInstance(bar2Type, ...)
}
};
var dynamicFoo = (dynamic)firstClass.FooList[0];
int i = dynamicFoo.NewProperty; // should be 56
Why don't you just use a Dictionary;
public class Foo
{
public Dictionary<string, object> Properties;
public Foo()
{
Properties = new Dictionary<string, object>();
}
}
public List<Foo> GetFoo()
{
var item = new Foo();
item.Properties.Add("Name","Sample");
item.Properties.Add("OtherName", "Sample");
return new List<Foo>{ item };
}
Adding property for a class in runtime, it is not possible to perform.