Let's say we have an entity class that looks like this:
public class SerializedEntity
{
public JsonDocument Payload { get; set; }
public SerializedEntity(JsonDocument payload)
{
Payload = payload;
}
}
According to npsql this generates a table with column payload of type jsonb for this class which is correct.
Now what I would like to do is take any class instance and store it as payload in this table e.g.:
public class Pizza {
public string Name { get; set; }
public int Size { get; set; }
}
should then be possible to be retrieved as an object with following structure:
{Name: "name", Size: 10}
So I need something like this:
var pizza = new Pizza("Margharita", 10);
var se = new SerializedEntity(someConverter.method(pizza))
You can use JsonSerializer.SerializeToDocument which was added in .NET 6. In your case you would end up with this:
var pizza = new Pizza("Margharita", 10);
var se = new SerializedEntity(JsonSerializer.SerializeToDocument(pizza))
With System.Text.Json it's a little awkward but possible:
using System.Text.Json;
using System.Text.Json.Serialization;
var pizza = new Pizza("Margharita", 10);
var se = new SerializedEntity(JsonDocument.Parse(JsonSerializer.Serialize(pizza)));
It has been built-in to dotnet core since (I think) v3.0, so you do not need any additional 3rd party libs. Just don't forget the usings.
There may be some tricks to get the parsing a little more efficient, though (using async API, perhaps or as Magnus suggests by serializing to binary using SerializeToUtf8Bytes).
I haven't been able to find any approach that goes directly from T or object to JsonDocument. And I cannot believe this is not possible somehow. Please leave a comment if you know how that works or add your answer.
Serailize it and than parse it to JsonDocument.
var doc = JsonDocument.Parse(JsonSerializer.SerializeToUtf8Bytes(
new Pizza {Name = "Calzone", Size = 10}));
If your EF entity (SerializedEntity) will always have a Pizza as its serialized JSON document, then you can simply use POCO mapping - replace the JsonDocument property with a Pizza property, and map it to a jsonb column.
If the actual types you want vary (sometimes Pizza, sometimes something else), you can also map an object property to jsonb, and assign whatever you want to it. Npgsql will internally serialize any object to JSON:
class Program
{
static void Main()
{
using var ctx = new BlogContext();
ctx.Database.EnsureDeleted();
ctx.Database.EnsureCreated();
ctx.FoodEntities.Add(new FoodEntity { SomeJsonFood = new Pizza { Name = "Napoletana" } });
ctx.FoodEntities.Add(new FoodEntity { SomeJsonFood = new Sushi { FishType = "Salmon" } });
ctx.SaveChanges();
}
}
public class BlogContext : DbContext
{
public DbSet<FoodEntity> FoodEntities { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql("...");
}
public class FoodEntity
{
public int Id { get; set; }
public string Name { get; set; }
[Column(TypeName = "jsonb")]
public object SomeJsonFood { get; set; }
}
public class Pizza
{
public string Name { get; set; }
public int Size { get; set; }
}
public class Sushi
{
public string FishType { get; set; }
}
Final solution for me:
public class SerializedEntity
{
public object? Payload { get; set; }
public SerializedEntity(object? payload)
{
Payload = payload;
}
}
and the EF configuration for it:
public void Configure(EntityTypeBuilder<SerializedEntity> builder)
{
builder.Property(n => n.Payload).HasColumnType("jsonb").HasConversion(
v => JsonConvert.SerializeObject(v,
new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}),
v => JsonConvert.DeserializeObject<object?>(v,
new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}));
}
Related
I have a program in .Net 5 C# that make a HTTP Request and the content (JSON) is deserialized (JsonNewtonsoft) in specific type:
// Class definition
public class FooSerializerModel {
[JsonProperty("latitude")]
public string Latitude { get; set; }
[JsonProperty("longitude")]
public string Longitude { get; set; }
}
// Deserialization
var fooJson = JsonConvert.DeserializeObject<FooSerializerModel>(response);
It works fine, now, I need cast the fooJson variable type to other type for pass it to Data Layer Method:
public class DataModel {
public string Latitude { get; set; }
public string Longitude { get; set; }
}
I need something like this:
var data = fromJson as DataModel;
But I get this error: Cannot convert type FooSerializerModel to DataModel Why?
I know is possible solve this problem using:
var data = new DataModel() {
Latitude = fromJson.Latitude,
Longitude = fromJson.Longitude
}
But if my class has 50 attributes I would have to assign one by one, it would be more lines of code and I could forget some of them when doing it explicitly.
Is it possible to do an implicit assignment on a single line? I have tried this:
var data = fromJson as DataModel; // Error
var data = (DataModel) fromJson; // Error
var data = (DataModel) (object) fromJson; // Exception 'Unable to cast object of type' when use 'dotnet run'
I remember that it is possible to do this in TypeScript if you work with types with interfaces and is easy!
All the code was tested in Visual Studio and working properly.
var fooJson = JsonConvert.DeserializeObject<DataModel>(response);
everything will be fine, since you don't really need [JsonProperty] attribute. You just need to fix your startup or there are another options to conver low case to upper.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver());
.... other code
}
but I even don't think that json attributes will affect your classes even if you use them for MongoDB. Classes are classes, but attributes are attributes, they live a completely separate life.
Another very popular way is to use AutoMapper
using AutoMapper;
.......
var mapper = new Mapper(new MapperConfiguration(x => x.AddProfile(new AutoMapperProfiles())));
DataModel data = mapper.Map<DataModel>(fooJson);
public class AutoMapperProfiles : Profile
{
public AutoMapperProfiles()
{
CreateMap<FooSerializerModel, DataModel>();
}
}
But if you like it hard way I can offer this
public class DataModel
{
public virtual string Latitude { get; set; }
public virtual string Longitude { get; set; }
}
public class FooSerializerModel:DataModel
{
[JsonProperty("latitude")]
public override string Latitude { get; set; }
[JsonProperty("longitude")]
public override string Longitude { get; set; }
}
Now you can write the code you wanted
var fooJson = JsonConvert.DeserializeObject<FooSerializerModel>(response);
DataModel data=fooJson;
I have a process that imports many CSV files with different definitions and converts them into a corresponding number of discrete POCO types based on various logic. In order to better troubleshoot problems, I now need to record the number of the row on the POCOs themselves. As such I've created a base class for all my POCOs with a single property to handle this.
As per this discussion it's possible to map the row/line number of a CSV file record to a POCO by constructing a ClassMap. However I'm currently using the IndexAttribute applied to each property in my POCO to perform the mapping. Since I have many POCOs, and mapping is currently working perfectly, I would prefer to not have to create ClassMaps for every POCO (not only is it more complex and IMO trickier to maintain, it's also more effort).
Is there a simple alternative, preferably using an attribute, to achieve row/line number => POCO property mapping? I looked through the attributes in the CSVHelper repo but didn't see anything that looks relevant.
You can use a TypeConverter attribute.
void Main()
{
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
HasHeaderRecord = false,
MissingFieldFound = null
};
using (var reader = new StringReader("1,One\n2,Two"))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<Foo>().Dump();
}
}
public class Foo
{
[Index(0)]
public int Id { get; set; }
[Index(1)]
public string Name { get; set; }
[TypeConverter(typeof(RowNumberConverter))]
public int RowNumber { get; set; }
}
public class RowNumberConverter : DefaultTypeConverter
{
public override object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
{
return row.Context.Parser.Row;
}
}
I had similar issue and this is what worked for me :
private class Foo
{
public int Id { get; set; }
public string Name { get; set; }
[LineNumber]
public int LineNumber { get; set; }
}
private class LineNumber : Attribute, IMemberMapper, IParameterMapper
{
public void ApplyTo(MemberMap memberMap)
{
memberMap.Data.ReadingConvertExpression = (ConvertFromStringArgs args) => args.Row.Parser.Row;
}
public void ApplyTo(ParameterMap parameterMap)
{
}
}
For more informations, check it here
If you use CsvHelper then you can simply do like this :
Map(m => m.LineNumber).Convert(row => row.Context.Row);
A full example will be like below:
void Main()
{
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
using (var reader = new StreamReader(stream))
using (var csv = new CsvReader(reader))
{
writer.WriteLine("Id,Name");
writer.WriteLine("1,one");
writer.WriteLine("2,two");
writer.Flush();
stream.Position = 0;
csv.Configuration.RegisterClassMap<TestMap>();
csv.GetRecords<Test>().ToList().Dump();
}
}
public class Test
{
public int Id { get; set; }
public string Name { get; set; }
public int RowNumber { get; set; }
}
public sealed class TestMap : ClassMap<Test>
{
public TestMap()
{
Map(m => m.Id);
Map(m => m.Name);
Map(m => m.RowNumber).Convert(row => row.Context.Row);
}
}
For the discussion and more information you can have look here.
Problem
Flatten large, multi level object using auto mapper to allow me to pass values to sql using Dapper.
Options
After reading the docs and various answers on SO, there seems to be two options, using ForMember or using NamingConventions. Neither of these can work for me as the object is so large that ForMember would require almost as much manual mapping. NamingConventions would mean some unreadable and irrelevant names for where the target object is being used.
refs I used: ForMember flattening
I did find a really basic example of using the ITypeConverter (Custom type converters) and have tried to make it work for me, but I'm really struggling, after three days of trying, I thought I should turn for help.
After searching various forums I decided to try and create a generic solution as I will be reusing this multiple times throughout my project.
Please note, if there is a better way (an existing extension, or a configuration i've missed, i'd love to hear of it. If not could you please offer some guidance on how to complete my task - I feel like I'm banging my head against a brick wall)
below I have added the console app I created to try to resolve this (I feel I should say that I know this code is awful, I have just been hacking away in the hope something would work)
using AutoMapper;
using System;
using System.Linq;
using System.Reflection;
namespace AutomapperTests
{
public class Program
{
static void Main(string[] args)
{
var property = Program.CreateProperty();
var config = new MapperConfiguration(cfg => cfg.CreateMap<PropertyDto, FlatProperty>()
.ConvertUsing<MyTypeConverter<PropertyDto, FlatProperty>>());
var mapper = config.CreateMapper();
var flatProperty = mapper.Map<FlatProperty>(property);
}
private static PropertyDto CreateProperty()
{
var property = new PropertyDto();
property.Guid = new Guid();//not mapping to guid
property.SomeTestVal = 123456;
property.TransactionStatus = TransactionStatus.ForSale;
property.Address = new AddressDto();
property.Detail = new DetailDto();
property.Address.PostCode1 = "ng10";
property.Detail.LandArea = 12;
return property;
}
}
public class MyTypeConverter<T, TK> : ITypeConverter<T, TK>
{
public TK Convert(T source, TK destination, ResolutionContext context)
{
var sourceType = source.GetType();
PropertyInfo[] sourceClassProperties = sourceType.GetProperties();//note: i've tried flattening the structure at this point but failed
var properties = GetCustomObjectsFromProperties(sourceType.GetProperties());
destination = (TK)Activator.CreateInstance(typeof(TK));
var destinationType = destination.GetType();
PropertyInfo[] destinationProperties = destinationType.GetProperties();
foreach (PropertyInfo sourceClassProperty in sourceClassProperties)
{
SetValue(sourceClassProperty, source, destinationProperties, destination);
}
return destination;
}
private void SetValue(PropertyInfo sourceClassProperty, T source, PropertyInfo[] destinationProperties, TK destination)
{
bool isNativeClass = IsNativeClass(sourceClassProperty);
object sourceValue = sourceClassProperty.GetValue(source);
string sourceName = sourceClassProperty.Name;
PropertyInfo destProperty = destinationProperties.FirstOrDefault(x => x.Name == sourceName);
if (destProperty == null && !isNativeClass)
{
//note: my idea was to use recursion to enter the object if necessary and search for a matching value ---maybe i'm really overthinking this???
}
SetDestinationValue(destProperty, destination, sourceValue);
}
private bool IsNativeClass(PropertyInfo sourceClassProperty)
{
return sourceClassProperty.GetType().Namespace == "System";
}
private void SetDestinationValue(PropertyInfo destProperty, TK destination, object sourceValue)
{
if (destProperty != null)
{
destProperty.SetValue(destination, sourceValue);
}
}
private PropertyInfo[] GetCustomObjectsFromProperties(PropertyInfo[] propertyInfo)
{
return propertyInfo.Where(info => info.Module.Name == GetAssemblyName()).ToArray();
}
private string GetAssemblyName()
{
return $"{typeof(T).Assembly.GetName().Name}.dll";
}
}
public class FlatProperty
{
public Guid Guid { get; set; }
public int SomeTestVal { get; set; }
public int? TransactionStatusId { get; set; }
public string Postcode1 { get; set; }
public decimal? LandArea { get; set; }
}
public class PropertyDto
{
public Guid Guid { get; set; }
public int SomeTestVal { get; set; }
public TransactionStatus? TransactionStatus { get; set; }
public AddressDto Address { get; set; }
public DetailDto Detail { get; set; }
}
public class AddressDto
{
public string PostCode1 { get; set; }
}
public class DetailDto
{
public decimal? LandArea { get; set; }
}
public enum TransactionStatus
{
Sold = 1,
ForSale = 2
}
}
Edit:
I have just found that it seems I can do something like this:
in config:
CreateMap<AddressDto, CreatePropertyDataModel>();
CreateMap<DetailDto, CreatePropertyDataModel>();
CreateMap<PropertyDto, CreatePropertyDataModel>()
.ForMember(dest => dest.Guid,
opts => opts.MapFrom(
src => src.Guid.ToString()));
set an object:
var property = new PropertyDto();
property.Guid = new Guid();//not mapping to guid
property.SomeTestVal = 123456;
property.TransactionStatus = TransactionStatus.ForSale;
property.Address = new AddressDto();
property.Detail = new DetailDto();
property.Address.PostCode1 = "ng10";
property.Detail.LandArea = 12;
return property;
in some class:
var dataModel = new UpsertPropertyDataModel();
_mapper.Map(_property, dataModel);
_mapper.Map(_property.Address, dataModel);
_mapper.Map(_property.Detail, dataModel);
My problem with this is that I get the error Unmapped members were found, these unmapped members are reffering to members Address or Detail could not map to, so I would have to set those members to Ignore(), is this a viable solution that i'm using incorrectly? (please note my real world objects are much bigger)
I want to serialize my model objects (from WPF MVVM) which contains pure data. This sounds easy but I don't want to use the Serialization attributes and stuff provided in .NET framework. I just want to serialize it using my own way.
So here's a simplified version of one of my classes.
public class EntryKeyValuePair
{
public EntryKeyValuePair(string key, string value, bool isMultiline = false, bool isMandatory = true, bool isProtected = false)
{
Key = key;
Value = value;
IsMultiline = isMultiline;
IsMandatory = isMandatory;
IsProtected = isProtected;
}
public string Key { get; set; }
public string Value { get; set; }
public bool IsMultiline { get; set; }
public bool IsMandatory { get; set; }
public bool IsProtected { get; set; }
public static EntryKeyValuePair FromXML(XElement element, ICipher cipher)
{
string key = cipher.Decrypt(element.Element(nameof(Key)).Value);
string value = cipher.Decrypt(element.Element(nameof(Value)).Value);
bool isMultiline = bool.Parse(element.Element(nameof(IsMultiline)).Value);
bool isMandatory = bool.Parse(element.Element(nameof(IsMandatory)).Value);
bool isProtected = bool.Parse(element.Element(nameof(IsProtected)).Value);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
public XElement ToXML(ICipher cipher)
{
return new XElement(nameof(EntryKeyValuePair),
new XElement(nameof(Key),cipher.Encrypt(Key)),
new XElement(nameof(Value), cipher.Encrypt(Value)),
new XElement(nameof(IsMultiline), IsMultiline), new XElement(nameof(IsMandatory), IsMandatory),
new XElement(nameof(IsProtected), IsProtected));
}
}
This works quite well. But this violates single responsibility principle and maybe other principles as well. This is also difficult to maintain and extend.
So I wanted to find another way. And here it is:
First I defined an IStringFormatter interface which can format the data to any string data formats like XML and JSON. (Not sure tho)
interface IStringFormatter
{
string Name { get; set; }
Dictionary<string, string> FieldDictionary { get; }
string Format();
}
Here's how the XMLStringFormatter looks like:
class XmlStringFormatter : IStringFormatter
{
public XmlStringFormatter()
{
FieldDictionary = new Dictionary<string, string>();
}
public string Name { get; set; }
public Dictionary<string, string> FieldDictionary { get; }
public string Format()
{
var xElement = new XElement(Name, FieldDictionary.Keys.Select(key => new XElement(key, FieldDictionary[key])));
return xElement.ToString();
}
}
Then I defined an ISerializer to serialize (or rather save) my data objects to the IStringFormatter.
interface ISerializer<T>
{
T DeSerialize(IStringFormatter stringFormatter);
void Serialize(T obj, IStringFormatter stringFormatter);
}
And here is how I "Serialize" EntryKeyValurPair by implementing this:
internal class EntryKeyValurPairSerializer : ISerializer<EntryKeyValuePair>
{
public EntryKeyValuePair DeSerialize(IStringFormatter stringFormatter)
{
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
try {
string key = fieldDictionary[nameof(EntryKeyValuePair.Key)];
string value = fieldDictionary[nameof(EntryKeyValuePair.Value)];
bool isMandatory = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMandatory)]);
bool isProtected = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsProtected)]);
bool isMultiline = bool.Parse(fieldDictionary[nameof(EntryKeyValuePair.IsMultiline)]);
return new EntryKeyValuePair(key, value, isMultiline, isMandatory, isProtected);
}
catch (KeyNotFoundException ex) {
throw new SerializationException(ex);
}
catch (FormatException ex) {
throw new SerializationException(ex);
}
}
public void Serialize(EntryKeyValuePair obj, IStringFormatter stringFormatter)
{
stringFormatter.Name = nameof(EntryKeyValuePair);
Dictionary<string, string> fieldDictionary = stringFormatter.FieldDictionary;
fieldDictionary.Add(nameof(EntryKeyValuePair.Key), obj.Key);
fieldDictionary.Add(nameof(EntryKeyValuePair.Value), obj.Value);
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMandatory), obj.IsMandatory.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsProtected), obj.IsProtected.ToString());
fieldDictionary.Add(nameof(EntryKeyValuePair.IsMultiline), obj.IsMultiline.ToString());
}
}
Now this works fine. But the problem is when I have a complex type like List<Entry> (where Entry is another data class) in my data classes.
As the IStringFormatter contains a Dictionary<string, string>, I can't just convert a List<Entry> to a string because I don't know what kind of IStringFormatter it wants in the context of ISerializer. How can I fix this? I also want to know if my solution is adhering to SOLID principles. If you can suggest a better solution (NOT the typical .NET serialization), I would appreciate it.
Writing your own serializer might be an interesting task, but I doubt that this is a good idea.
As I understood you want to keep your models clean, without any serialization specific attributes. I guess by "typical .NET serialization" you mean methods included with .Net framework.
For simplicity we will use these simple classes as an example:
class Customer
{
public string Name { get; set; }
public int Age { get; set; }
public List<Order> Orders { get; set; }
}
class Order
{
public int Id { get; set; }
public string Details { get; set; }
}
An easy option would be to use Json.NET:
var customer = new Customer
{
Name = "Darth Vader",
Age = 45,
Orders = new List<Order>
{
new Order { Id = 1, Details = "Order1" },
new Order { Id = 2, Details = "Order2" }
}
};
string json = JsonConvert.SerializeObject(customer);
So as you can see you don't need to add any custom attributes to Customer class. It will work until you want to serialize all public properties.
The resulting JSON will be:
{
"Name": "Darth Vader",
"Age": 45,
"Orders": [
{
"Id": 1,
"Details": "Order1"
},
{
"Id": 2,
"Details": "Order2"
}
]
}
After that you can always deserialize it:
var customer = JsonConvert.DeserializeObject<Customer>(json);
Lets say that you don't want Age property to be included. In this case I would suggest to create a different class that will be used for serialization only:
class CostomerSerializationContract
{
public string Name { get; set; }
public List<Order> Orders { get; set; }
}
This main advantage if this approach is that you have separate class for serialization, and you can add any custom attributes there, if you choose to use some other serializer, without violating SOLID principle. The main disadvantage is that you need to keep both objects in sync manually.
You can use AutoMapper to reduce manual work when creating serialization contract from source class.
I have a similar structure to the one below
Base class
public class BaseClass
{
public string Name { get; set; }
public string Address { get; set; }
public int Age { get; set; }
public Guid Guid { get; set; }
public string Hometown { get; set; }
}
Derived Class
public class DerivedClass : BaseClass
{
public List<DerivedClassDataItem> Data { get; set; }
}
Data class
public class DerivedClassDataItem
{
public string Datum1 { get; set; }
public string Datum2 { get; set; }
public string Datum3 { get; set; }
public string Datum4 { get; set; }
public int Datum5 { get; set; }
public DateTime Datum6 { get; set; }
}
What is the best practice to return specific set of info from the DerivedClass?
a potential set could be:
Name, Address, Guid and then a Data list that only contains Datum1 and Datum4
I could see anonymousTypes, Tuples or another set of class(es), all to be valid approaches.
My concern about creating new set of classs for the set returned is that the class(s) structure will be similar to the structure of the three mentioned above except it will have fewer selected members, which to me, does not sound ideal. (duplicate code and structure)
Using anonymousTypes was my initial solution to tackle this, something like
List<DerivedClass> list = new List<DerivedClass>();
var mySet = list.Select(d => new
{
Name = d.Name,
Address = d.Address,
.
.
.
.
.
Data = d.Data.Select(item => new
{
Datum1 = item.Datum1,
Datum4 = item.Datum4
})
});
but again, that was a headache for us to track through httpResponse and through out API calls.
Should I go with Tuple?
Any insights as to what is the best practice for doing this?
Edit
I am using this set of data to be a response returned by a API/GET call. I will send the set back using HttpRespose and then the framework will transform that into json
this is an actual method we have now
private void populateReturnFile()
{
var returnFileAnonymous = new
{
Vendor = this.Vendor,
OrganizationName = this.OrganizationName,
User = this.User,
Platform = this.Platform,
DictionaryType = this.DictionaryType,
UseCaseId = this.UseCaseId,
Data = this.Data.Select(d => new
{
MigrationTermId = d.MigrationTermId,
ImoLexicalCode = d.ImoLexicalCode
})
};
this.returnFile = returnFileAnonymous;
}
Then my GET will return the retunFile (this is a very simple method, i have remove irrelevant code)
[HttpGet]
public HttpResponseMessage Get(Guid migrationFileId)
{
ProblemList problemList = ProblemList.GetProblemList(migrationFileId);
return Request.CreateResponse(HttpStatusCode.OK, problemList.ReturnFile, new JsonMediaTypeFormatter());
}
If API calls is where you are using these classes, then I personally like to keep it simple and avoid complex inheritance hierarchy. Remember, simple code is good code.
I would make a separate class for each api request/response call. For very simple api calls (ajax requests for example) I like to use anonymous types, but for controllers that only handle API calls I like to create separate classes, organized in a nice folder structure.
Everyone has their "style" but as long as you strive for simplicity your code will be maintainable.