I am using MetadataType to define Json.NET attributes for the following type, then serializing it using Json.NET inside its ToString() method:
namespace ConsoleApp1
{
public interface ICell
{
int Id { get; }
}
public interface IEukaryote
{
System.Collections.Generic.IEnumerable<ICell> Cells { get; }
string GenericName { get; }
}
public sealed partial class PlantCell
: ICell
{
public int Id => 12324;
}
public sealed partial class Plant
: IEukaryote
{
private readonly System.Collections.Generic.IDictionary<string, object> _valuesDict;
public Plant()
{
_valuesDict = new System.Collections.Generic.Dictionary<string, object>();
var cells = new System.Collections.Generic.List<PlantCell>();
cells.Add(new PlantCell());
_valuesDict["Cells"] = cells;
_valuesDict["GenericName"] = "HousePlant";
}
public System.Collections.Generic.IEnumerable<ICell> Cells => _valuesDict["Cells"] as System.Collections.Generic.IEnumerable<ICell>;
public string GenericName => _valuesDict["GenericName"] as string;
public int SomethingIDoNotWantSerialized => 99999;
public override string ToString()
{
return Newtonsoft.Json.JsonConvert.SerializeObject(this,
new Newtonsoft.Json.JsonSerializerSettings()
{
ContractResolver = new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver()
}
);
}
}
[System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
public sealed partial class Plant
{
[Newtonsoft.Json.JsonObject(Newtonsoft.Json.MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[Newtonsoft.Json.JsonProperty]
public System.Collections.Generic.IEnumerable<ICell> Cells;
[Newtonsoft.Json.JsonProperty]
public string GenericName;
//...
}
}
class Program
{
static void Main(string[] args)
{
var plant = new Plant();
System.Console.WriteLine(System.String.Format("Output is {0}", plant.ToString()));
System.Console.ReadKey();
}
}
}
My problem is that Plant.ToString() will return '{}'. Why is that? It was working before. The only change I made was in PlantMetadata where I altered the MemberSerialization to OptIn instead of OptOut, as I had less properties I wanted included than left out.
As stated by Newtonsoft in this issue, MetadataTypeAttribute attributes are in fact supported by Json.NET. However, it appears that Json.NET requires that the MetadataClassType members must be properties when the corresponding "real" members are properties, and fields when the corresponding "real" members are fields. Thus, if I define your Plant type as follows, with two properties and one field to be serialized:
public sealed partial class Plant : IEukaryote
{
public System.Collections.Generic.IEnumerable<ICell> Cells { get { return (_valuesDict["Cells"] as System.Collections.IEnumerable).Cast<ICell>(); } }
public string GenericName { get { return _valuesDict["GenericName"] as string; } }
public string FieldIWantSerialized;
public int SomethingIDoNotWantSerialized { get { return 99999; } }
// Remainder as before.
Then the PlantMetadata must also have two properties and one field for them to be serialized successfully:
//Metadata.cs
[System.ComponentModel.DataAnnotations.MetadataType(typeof(PlantMetadata))]
public sealed partial class Plant
{
[JsonObject(MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[JsonProperty]
public IEnumerable<ICell> Cells { get; set; }
[JsonProperty]
public string GenericName { get; set; }
[JsonProperty]
public string FieldIWantSerialized;
}
}
If I make Cells or GenericName be fields, or FieldIWantSerialized be a property, then they do not get opted into serialization.
Sample working .Net Fiddle.
Note that, in addition, I have found that the MetadataClassType properties apparently must have the same return type as the real properties. If I change your PlantMetadata as follows:
[JsonObject(MemberSerialization.OptIn)]
internal sealed class PlantMetadata
{
[JsonProperty]
public object Cells { get; set; }
[JsonProperty]
public object GenericName { get; set; }
[JsonProperty]
public object FieldIWantSerialized;
}
Then only FieldIWantSerialized is serialized, not the properties. .Net Fiddle #2 showing this behavior. This may be a Newtonsoft issue; as stated in the Microsoft documentation Defining Attributes in Metadata Classes:
The actual type of these properties is not important, and is ignored
by the compiler. The accepted approach is to declare them all as of
type Object.
If it matters, you could report an issue about the return type restriction to Newtonsoft - or report an issue asking that details of their support for MetadataTypeAttribute be more fully documented.
Related
Is there a way to deserialize an integer into a string ? I need it for compatibility reason.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Abc.Test
{
[JsonSerializable(typeof(OrderInfo), GenerationMode = JsonSourceGenerationMode.Metadata)]
public partial class OrderInfoContext : JsonSerializerContext
{ }
public partial class OrderInfo
{
public string UserReference { get; set; }
}
public class Program
{
static void Main(string[] args)
{
var json = #"{""UserReference"": 123}"; // <---- how having 123 deserialize as a string?
var s = JsonSerializer.Deserialize(json, OrderInfoContext.Default.OrderInfo);
}
}
}
In some cases it can make sense to separate your serialization objects (aka DTOs) from your domain objects. This give you several benefits:
Allow your domain objects to have behavior defined, without affecting serialization.
A place to handle any complicated changes to the model without losing backwards compatibility.
Allow the serialization objects to fulfill requirements like public setters, without affecting the usage in the rest of the code.
Ex:
public class OrderInfoDTO
{
public int UserReference { get; set; }
public OrderInfo ToModel() => new OrderInfo(UserReference.ToString();
}
public class OrderInfo{
public string UserReference {get;}
public OrderInfo(string userReference) => UserReference = userReference;
}
You can use a custom converter on a property. I'll look something like:
public partial class OrderInfo
{
[JsonConverter(typeof(YourCustomConverter))]
public string UserReference { get; set; }
}
Is it possible to specify that I always want type-information in the json object when serializing a property in an class?
(Ideally with Newtonsoft).
I'm thinking something like this:
public abstract class Value {...}
public class BigValue : Value {...}
public class SmallValue : Value {...}
public class ValueContainer
{
[JsonSetting(TypenameHandling = TypenameHandling.All)] // <--- Something like this?
public Value TheValue { get; set; }
}
I am aware that I could specify this behavior when doing the parsing with a custom converter.
But I want to include the typeinformation every time objects of this type is serialized, without manually having to specify which serialization options to use.
Newtonsoft.Json's JsonPropertyAttribute has TypeNameHandling property which you can set:
public class Root
{
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public Base Prop { get; set; }
}
public class Base
{
public int IntProp { get; set; }
}
public class Child:Base
{
}
// Example:
var result = JsonConvert.SerializeObject(new Root
{
Prop = new Child()
});
Console.WriteLine(result); // prints {"Prop":{"$type":"SOAnswers.TestTypeNamehandling+Child, SOAnswers","IntProp":0}}
I am looking for an elegant way of statically referencing a property attribute in C#. To give you an example, say I have this class:
public class A
{
[Attribute(Name="myAttributeName")]
public string Property1 { get; set; }
}
Now, I see the attribute as quite similar to a static member of a class, so in my mind, there should be an easy way to access the attribute from outside the class; e.g. through a similar operator to typeof or nameof (but it would return a list of attributes, since there may be multiple attributes to fetch). The way I would like to use this operator is as follows:
public class B
{
// Through an attribute definition
[Attribute2(attrof(A.Property1))]
public string Property2 { get; set; }
// In a method
public void method()
{
var attrs = attrof(A.property1);
}
}
I think I have found one way to make it work with two parameters like the example below - at least for the method invocation. Passing variables to attributes doesn't seem to work in C#, but that's nevertheless the way I'd like to construct my code.
public class C
{
public static object[] GetAttrs(Type type, string propertyName)
{
return type.GetProperty(propertyName).GetCustomAttributes(true);
}
}
public class A
{
[Attribute1(Name="myAttributeName")]
public string Property1 { get; set; }
}
public class B
{
// Through an attribute definition
// Unfortunately, passing variable to attrs not supported
// so this does not work
[Attribute2(C.GetAttrs(typeof(A), nameof(A.Property1)))]
public string Property2 { get; set; }
// In a method
public void method()
{
var attrs = C.GetAttrs(typeof(A), nameof(A.Property1));
}
}
However, it feels tedious to pass references to both the class and property, when syntactically, A.Property1 contains information about both - something a compiler should be able to draw information from. Therefore, I wonder if any such operator exists today, or if there are any other ideas on how this functionality could be achieved?
EDIT: I just thought about the B.Property2 attribute definition one more time and thought that it should still be possible to get this working, since I think attributes are constant. Or am I missing something here?
There is no default operator for such case, but you could implement something similar. Code to extract value from A.Property1 attribute is in Main function
using System;
using System.Reflection;
namespace ConsoleApp16
{
public class CustomAttribute : Attribute
{
public string Name { get; }
public CustomAttribute(string name)
{
Name = name;
}
}
public class ReferenceAttribute : Attribute
{
public string PropertyName { get; }
public Type Type { get; }
public ReferenceAttribute(Type type, string propertyName)
{
Type = type;
PropertyName = propertyName;
}
}
public class A
{
[Custom("text")]
public string Property1 { get; set; }
}
public class B
{
[Reference(typeof(A), nameof(A.Property1))]
public string Property { get; set; }
}
class Program
{
static void Main(string[] args)
{
var referenceAttribute = typeof(B).GetProperty(nameof(B.Property))
.GetCustomAttribute<ReferenceAttribute>();
var customAttribute = referenceAttribute.Type.GetProperty(referenceAttribute.PropertyName)
.GetCustomAttribute<CustomAttribute>();
Console.WriteLine(customAttribute.Name);
}
}
}
I'm still fairly new to programming and have been tasked with creating a WebHook consumer that takes in a raw JSON string, parses the JSON into an object, which will be passed into a handler for processing. The JSON is coming in like this:
{
"id":"1",
"created_at":"2017-09-19T20:41:23.093Z",
"type":"person.created",
"object":{
"id":"person1",
"created_at":"2017-09-19T20:41:23.076Z",
"updated_at":"2017-09-19T20:41:23.076Z",
"firstname":"First",
...
}
}
The inner object can be any object so I thought this would be a great opportunity to use generics and built my class as follows:
public class WebHookModel<T> where T : class, new()
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "created_at")]
public DateTime CreatedAt { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "object")]
public T Object { get; set; }
[JsonIgnore]
public string WebHookAction
{
get
{
return string.IsNullOrEmpty(Type) ? string.Empty : Type.Split('.').Last();
}
}
}
Then created the following interface:
public interface IWebHookModelFactory<T> where T : class, new()
{
WebHookModel<T> GetWebHookModel(string type, string jsonPayload);
}
What I'm failing to understand is how am I supposed to implement the Factory class without knowing what the type is at compile time?
Playing around with the Model a bit, I changed it to an abstract class with an abstract T object so that it could be defined by a derived class.
public abstract class WebHookModel<T> where T : class, new()
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "created_at")]
public DateTime CreatedAt { get; set; }
[JsonProperty(PropertyName = "type")]
public string Type { get; set; }
[JsonProperty(PropertyName = "object")]
public abstract T Object { get; set; }
[JsonIgnore]
public string WebHookAction
{
get
{
return string.IsNullOrEmpty(Type) ? string.Empty : Type.Split('.').Last();
}
}
}
public PersonWebHookModel : WebHookModel<Person>
{
public override Person Object { get; set; }
}
But I still run into the same issue of trying to implement an interface in which I don't know the type at runtime. From what I've found online, this is an example of covariance, but I haven't found any articles that explain how to resolve this issue. Is it best to skip generics and create a massive
case statement?
public interface IWebHookFactory<TModel, TJsonObject>
where TJsonObject : class, new()
where TModel : WebHookModel<TJsonObject>
{
TModel GetWebHookModel(string type, string jsonPayload);
}
I'm a bit partial to using the abstract class approach because it lets me define individual handlers based on which model I'm passing into my Service.
public interface IWebHookService<TModel, TJsonObject>
where TJsonObject : class, new()
where TModel : WebHookModel<TJsonObject>
{
void CompleteAction(TModel webHookModel);
}
public abstract class BaseWebhookService<TModel, TJsonObject> : IWebHookService<TModel, TJsonObject>
where TJsonObject : class, new()
where TModel : WebHookModel<TJsonObject>
{
public void CompleteAction(TModel webHookModel)
{
var self = this.GetType();
var bitWise = System.Reflection.BindingFlags.IgnoreCase
| System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.NonPublic;
var methodToCall = self.GetMethod(jsonObject.WebHookAction, bitWise);
methodToCall.Invoke(this, new[] { jsonObject });
}
protected abstract void Created(TModel webHookObject);
protected abstract void Updated(TModel webHookObject);
protected abstract void Destroyed(TModel webHookObject);
}
public class PersonWebHookService : BaseWebHookService<PersonWebHookModel, Person>
{
protected override void Created(PersonWebHookModel webHookModel)
{
throw new NotImplementedException();
}
protected override void Updated(PersonWebHookModel webHookModel)
{
throw new NotImplementedException();
}
protected override void Destroyed(PersonWebHookModel webHookModel)
{
throw new NotImplementedException();
}
}
Key points for the solution:
1. There needs to be some virtual call in there somewhere.
2. Somehow you need to map from your type tag in your JSON payload to your actual C# class.
IE, "person.created"," --> 'Person'.
If you control the serialization format, JSON.Net can inject its own type tag and do this for you. Assuming you can't go that route ...
So you'll need something like a Dictionary to contain the mapping.
Assuming your definitions is like:
abstract class WebhookPayload // Note this base class is not generic!
{
// Common base properties here
public abstract void DoWork();
}
abstract class PersonPayload : WebhookPayload
{
public override void DoWork()
{
// your derived impl here
}
}
And then you can deserialize like:
static Dictionary<string, Type> _map = new Dictionary<string, Type>
{
{ "person.created", typeof(PersonPayload)}
}; // Add more entries here
public static WebhookPayload Deserialize(string json)
{
// 1. only parse once!
var jobj = JObject.Parse(json);
// 2. get the c# type
var strType = jobj["type"].ToString();
Type type;
if (!_map.TryGetValue(strType, out type))
{
// Error! Unrecognized type
}
// 3. Now deserialize
var obj = (WebhookPayload) jobj.ToObject(type);
return obj;
}
I am using Newtonsoft's JsonSerializer to serialise some classes.
As I wanted to omit one field of my class in the serialisation process, I declared it as follow:
[JsonIgnore]
public int ParentId { get; set; }
This worked, but I am now facing a new problem : In a derived class, I would like this field to appear (and do so only in this specific derived class).
I have been looking through the documentation and on the Internet for a way to override this setting in child classes (I guess I need something like [JsonStopIgnore], but I couldn't find anything close).
Is there any way for me to force JsonSerializer to pick up again this attribute ?
Is it possible to explicitly mark an attribute as [JsonIgnore], but only in base class ?
The only way to "override" the behavior of the [JsonIgnore] attribute is to use a contract resolver, as #Yuval Itzchakov nicely explained in his answer.
However, there is another possible solution that might work for you: instead of using a [JsonIgnore] attribute, you could implement a ShouldSerializeParentId() method in your classes to control whether the ParentId property gets serialized. In the base class, make this method return false; then, override the method in the derived class to return true. (This feature is known as conditional property serialization in Json.Net.)
public class Base
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual bool ShouldSerializeParentId()
{
return false;
}
}
public class Derived : Base
{
public override bool ShouldSerializeParentId()
{
return true;
}
}
Fiddle: https://dotnetfiddle.net/65sCSz
You can do this by creating a custom DefaultContractResolver and overriding its CreateProperty method.
For example, given a Foo base and a derived Bar:
public class Foo
{
[JsonIgnore]
public string Name { get; set; }
public int Age { get; set; }
}
public class Bar : Foo
{ }
You can create the following contract resolver:
public class MyTypeContractResolver<T> : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member,
MemberSerialization
memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
property.Ignored = false;
property.ShouldSerialize = propInstance => property.DeclaringType != typeof (T);
return property;
}
}
This will set all properties to Ignored = false, and then analyze them by the given predicate:
propInstance => property.DeclaringType != typeof (T);
Which in our case means "you should serialize only if they are not of type Foo" (since Foo is the DeclaryingType).
And then when you want to deserialize, you pass an instance of the contract resolver to JsonSerializerSettings:
var bar = new Bar();
var result = JsonConvert.SerializeObject(bar,
new JsonSerializerSettings {ContractResolver = new MyTypeContractResolver<Bar>()});
I solved the same problem by using the new keyword on the property of the derived class.
public class Foo
{
[JsonIgnore]
public int ParentId { get; set; }
}
public class Bar: Foo
{
[JsonProperty("ParentId")]
public new int ParentId { get; set; }
}
You can probably simply overwrite ParentId in the derived class.
public new int ParentId
{
get { return base.ParentId; }
set { base.ParentId = value; }
}
I solved the same problem with a ghost property :
public class Foo
{
[JsonIgnore]
public int ParentId { get; set; }
[NotMapped]
public int FooParent { get; set; }
}
When I want to show this property quite always hidden, I populate it, other times it is null :
Foos.ForEach(x => x.FooParent = ParentId);