So, I'm not sure where my head is at today but I can't wrap my head around a decent solution to this issue and I was hoping you guys can help.
We are building a .NET Core / EF Core / SQL application that hands a base record type, and then we layer on metadata objects on top of that base record to enrich the datasets. So my base class looks something like this:
public class Record
{
[NotNull]
public Guid Id { get; set }
[CanBeNull]
public virtual string InvoiceNum { get; set; }
[CanBeNull]
public virtual string Notes { get; set; }
//Enum that defines the extra properties
public virtual RecordType RecordType { get; set; }
public Dictionary<string,object> ExtraProperties { get; set; }
}
and my MetaData class looks something like this:
public class MetaData
{
[NotNull]
public Guid Id { get; set; }
[NotNull]
public Guid RecordId { get; set; }
[NotNull]
public virtual string Description { get; set; }
//Enum that defines the extra properties
public virtual MetaDataTypes MetaDataType { get; set; }
public Dictionary<string,object> ExtraProperties { get; set; }
}
I am using the ExtraProperties field in each object to store a JSON encoded string of additional data for each type, determined by the Enum values noted in the objects above.
What I am trying to find out, is how do I consistently (and efficiently) define those metadata fields on an incoming JSON without needing to hard code each individual type?
For example, I was thinking the JSON for a complete incoming record could be something like this:
{
"Record" : {
"InvoiceNum":"12345",
"Notes":"Notes go here",
"MetaData" : [
{
"MetaDataType" : "Address",
"Properties" :
{
"Address01":"123 ABC Ave",
"City":"New York City",
"Country":"US"
}
},
{
"MetaDataType":"ClientContact",
"Properties" :
{
"FirstName":"John",
"LastName":"Doe",
"Email":"jdoe#example.com",
"Phone":"8675309"
}
}]
}
}
But I want the "Properties" objects to have consistent values based on what the MetaDataType object is defined as and I only want to store the properties for that particular object type in the database attached to that object (there are, across all metadata types, about a hundred unique values).
Hopefully that makes sense, like I said earlier my brain is fried a little today so maybe I am missing something really obvious, but I would like to get your thoughts.
Related
I have a class with the following fields.
public class Payment
{
public string type { get; set; }
public string issuer { get; set; }
public string identifier { get; set; }
public float price { get; set; }
}
The "type" field can be "Giftcard" or "Creditcard".
I want to serialize it depends on the "type" field.
{
"type": "Giftcard",
"giftcard_no": "111111111111",
"giftcard_price": 100
}
{
"type": "Creditcard",
"issuer": "AMEX",
"last_4_digits": "1000",
"creditcard_price": 100
}
As you can see, the field names are different depends on the "type" field.
And "issuer" field is ignored in case of Giftcard.
I've found similar questions, but couldn't find the correct answer.
I will be appreciated for any help.
Thank you.
It sounds to me like what you want is different subclasses, using type to determine which one to use when deserializing. I've found that the JsonSubTypes package (GitHub repo) works very well for this.
You'd have something like this:
[JsonConverter(typeof(JsonSubtypes), "type")]
[JsonSubtypes.KnownSubType(typeof(GiftCard), "Giftcard")]
[JsonSubtypes.KnownSubType(typeof(CreditCard), "Creditcard")]
public class Payment
{
[JsonProperty("type")]
public virtual string Type { get; }
}
public class CreditCard : Payment
{
public override string Type => "Creditcard";
[JsonProperty("issuer")
public string Issuer { get; set; }
// Etc, other properties
}
public class GiftCard : Payment
{
public override string Type => "Giftcard";
[JsonProperty("giftcard_no")]
public string GiftCardNumber { get; set; }
// Etc, other properties
}
There are various different options for exactly how you register things - the README on the GitHub repo gives concrete examples, which is really useful.
You'd then deserialize to Payment, but the returned value would be a reference to an instance of GiftCard or CreditCard.
I have around 500 class files (.cs) and all of them have have more or less the following structure
public class InvoiceHeader
{
public string Name { get; set; }
public string ProductName { get; set; }
public int Costs { get; set; }
public int InvoiceID { get; set; }
......
......
}
All properties are only built-in c# types (string, int(?), long(?), bool, DateTime,..). No lists or custom types.
Sometimes a class has over 300 properties and I want to filter e.g all ID properties and generate a new .cs file which has all properties exactly like the original one, excluding all properties ending with "ID" (as an example). Is there any way to achieve this? I have literally no idea how to start. I appreciate any help!
I am writing a set of data structures to ingest third-party JSON into (no writing out) using JSON.NET.
I have a case for reading some of the top-level JSON elements into a member object of the object being deserialized into.
My JSON:
{
"Id":1
"Checksum":42
"Name":"adam",
"Hair":true
}
My ideal object structure:
public class EntityHeader
{
int Id { get; set; }
int Checksum { get; set; }
}
public class Entity
{
[HeroicJsonAttribute( "Id", "Checksum" )]
public EntityHeader Header { get; set; }
public string Name { get; set; }
public bool Hair { get; set; }
}
Is there a simple way to achieve this? I will have a number of types which will need this, and I'd hate to have to write a JsonConverter for each.
This question has been asked before, here, but the accepted answer doesn't address the question.
Thanks!
An alternative approach would be to use an EntityHeader field in the Entity class as a backing store for private properties which can be deserialized into:
public class EntityHeader
{
int Id { get; set; }
int Checksum { get; set; }
}
public class Entity
{
private EntityHeader m_Header = new EntityHeader();
public EntityHeader Header { get { return m_Header; } }
[JsonProperty]
private int Id { set { m_Header.Id = value; } }
[JsonProperty]
private int Checksum { set { m_Header.Checksum = value; } }
public string Name { get; set; }
public bool Hair { get; set; }
}
Thus, all the properties in the JSON can be read straight into the Entity object, but consumers of Entity objects have access to a "nicely encapsulated" EntityHeader property.
I haven't tested this, and it may even be kludgey, but it would technically work for me (OP). I am still interested in other answers!
Base on your example you could either; use the adapter pattern:
public class EntityJson
{
int Id { get; set; }
int Checksum { get; set; }
public string Name { get; set; }
public bool Hair { get; set; }
}
// quick/poor example
public class EntityAdapter : IEntity
{
public EntityAdapter(EntityJson model)
{
Header = new Header(); // and populate this objects fields
Name = model.Name; // populate other properties
}
public EntityHeader Header { get; set; }
public string Name { get; set; }
public bool Hair { get; set; }
}
Or abuse the fact that json.net ignores properties not available:
var entity = JsonConvert.Deserialze<Entity>();
var header = JsonConvert.Deserialize<EntityHeader>();
entity.Header = header;
I'm going to go ahead and post this answer which is a little bit too long for a comment, so please take this more as an extended comment than an actual attempt to answer your specific question. And of course, you know your requirements best so this is just my considered opinion :)
With that in mind, my advice is:
Don't do this.
I would instead create a simple DTO class that has a 1-1 relationship to the JSON being received; and I'd put all my validation attributes on the properties of that class.
Once I had deserialised the JSON into this simple DTO, I would then use a mapping layer of some kind (roll your own or use Automapper, etc) to map this DTO into a more meaningful structure such as your Entity class.
My reasoning behind this is because unless your Entity class is itself only a simple DTO (in which case it should be as simple as possible and ideally not be a composite) you are mixing OOP and concerns with data mapping concerns; whilst this in and of itself is not such a bad thing, it only serves to increase the complexity of your code.
Consider for example if your incoming JSON ends up with 30 or 40 properties, and you manage to figure out a way (maybe adapting some of the nice techniques from the other answers) to map it to the Entity class. But what about when something goes wrong - it's going to be much easier to reason about, and therefore debug, a process which you have much more control over; it's also going to be much easier to make special adaptations for odd edge cases where the serialiser behaviour just can't help you out
Granted it's a bit of work to write and maintain these DTOs but not that much - Webtools already does this for you
Reference: At the boundaries, Applications are not Object-Oriented
Background:
I have a custom class, which represents a Data Base Table, each property corresponding to a table column. The properties can be classified in three ways.
Example: Take for example a Person object.
MetaProperties: (Columns that are needed by the program)
Person_ID: used in table for indexing etc...
UserDefinedType: (UDT), complex class handling write-permission on the table.
Timestamp: needed to handle the UDT in C# DataTables
RealProperties: (actual traits that describe the real Person)
FullName
DateOfBirth
PlaceOfBirth
EyeColor
etc... (many more)
RawDataProperties: (these columns hold raw data from external sources)
Phys_EyeColor: the eye-color, as directly imported from the physical traits database, may be in unknown format, may have conflicting value with entry from other db, or any other data quality issue...
HR_FullName: full name as given in HR file
Web_FullName: full name as taken from a web form
Web_EyeColor: eye color as taken from web form
etc...
public class Person
{
#region MetaProperties
public int Person_ID { get; set; }
public UserDefinedType UDT { get; set; }
public DateTime timestamp { get; set; }
#endregion
#region RealProperties
public string FullName { get; set; }
public DateTime DateOfBirth { get; set; }
public string PlaceOfBirth { get; set; }
public Color EyeColor { get; set; }
//...
#endregion
#region RawDataProperties
public string Phys_EyeColor { get; set; }
public string Phys_BodyHeight { get; set; }
public string Web_FullName { get; set; }
public string Web_EyeColor { get; set; }
public string HR_FullName { get; set; }
//...
#endregion
}
Question: How can I programmatically differentiate between these three types of properties in my Person class? The goal is to be able to iterate through properties of a certain type using System.Reflection or some other organisational construct. Pseudocode:
foreach(Property prop in Person.GetPropertiesOfType("RealProperty"){
... doSmth(prop);
}
I'm thinking about writing custom Attributes, and hanging them on to the properties, sort of like taggin.
But since I know nothing about custom Attributes, I would like to ask if I'm going down the proper path, or if there are any other better ways of doing this.
Note: the shown example may may not be the best in terms of program design, and I am well aware that inheritance or splitting up the class otherwise could solve this problem. But that is not my question - I want to know if properties in a class can be tagged or somehow differentiated between using custom categories.
You can do this with custom attributes.
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class PropertyAttribute : System.Attribute
{
public PropertyType Type { get; private set; }
public PropertyAttribute (PropertyType type) { Type = type; }
}
public enum PropertyType
{
Meta,
Real,
Raw,
}
Then, you can do this with each property or field:
[PropertyType(PropertyType.Meta)]
public int Person_ID;
[PropertyType(PropertyType.Real)]
public string FullName;
[PropertyType(PropertyType.Raw)]
public string Phys_EyeColor;
Then you can access it with something like
foreach (PropertyAttribute attr in this.GetType().GetCustomAttributes(typeof(PropertyAttribute), false))
{
// Do something based on attr.Type
}
I'm trying to map JSON that looks like
"ids": {
"id": {
"#value":"6763754764235874140"
}
}
And I'd like to map it onto a couple of classes that look like
class Property
{
public Ids Ids { get; set; }
}
class Ids
{
public string Id { get; set; }
}
So basically I want to stuff the value of ids/id/#value from the JSON document into Ids.Id in the class architecture. From browsing the documentation, I thought I could use something like
[JsonProperty(ItemConverterType=typeof(IdConverter))]
public string Id { get; set; }
and provide a custom JsonConverter subclass named IdConverter. When I do, though, my IdConverter.ReadJson never gets called. What am I doing wrong?
Looks like the answer was that ItemConverterType is for converting items in an array. Double-annotating the property with JsonProperty and JsonConverter attributes works:
[JsonProperty(PropertyName="id")]
[JsonConverter(typeof(IdConverter))]
public string Id { get; set; }