I'm storing data in a Firestore DB using .net. I'm using FirestoreData and FirestoreProperty attributes to control how objects are serialized to the DB. C# propeties, by default, are in PascalCase and I'd like them to be serialized in camelCase. I know I can set the name a property will be serialized in the FirestoreProperty attribute, but it's a really tedious and error proner task. Is there any way to configure Firestore .net client to by default serialize properties in camelCase?
Thanks
The FirestorePropertyAttribute defines two constructors. One allows to add a name by providing the parameter name:
The name to use within the Firestore document.
So you can simply set it for a property like
[FireStoreProperty("anyCase")]
public string AnyCase{ get; set; }
Doing this a silent way is not possible without modifying the underlying type. A possible approach is to implement a reflection based Document converter, changing the property names at runtime. You only need to define the converter once for each data class. Here is a possible approach:
//Sample data class
[FirestoreData(ConverterType = typeof(CamelCaseConverter<CustomCity>))]
public class CustomCity
{
public string Name { get; set; }
public string Country { get; set; }
public long Population { get; set; }
}
//Sample data class
[FirestoreData(ConverterType = typeof(CamelCaseConverter<CustomPerson>))]
public class CustomPerson
{
public string Name { get; set; }
public uint Age { get; set; }
}
//Conversion of camelCase and PascalCase
public class CamelCaseConverter<T> : IFirestoreConverter<T> where T : new()
{
public object ToFirestore(T value)
{
dynamic camelCased = new ExpandoObject();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
string camelCaseName =
char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1);
((IDictionary<string, object>)camelCased)[camelCaseName] = property.GetValue(value);
}
return camelCased;
}
public T FromFirestore(object value)
{
if (value is IDictionary<string, object> map)
{
T pascalCased = new T();
foreach (PropertyInfo property in typeof(T).GetProperties())
{
string camelCaseName =
char.ToLowerInvariant(property.Name[0]) + property.Name.Substring(1);
property.SetValue(pascalCased, map[camelCaseName]);
}
return pascalCased;
}
throw new ArgumentException($"Unexpected data: {value.GetType()}");
}
Related
I have the below Model class,
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
public class FormField
{
[Required]
[JsonPropertyName("STD_USERTYPEID")]
public string UserTypeId { get; set; }
[Required]
[JsonPropertyName("STD_OFFICETYPEID")]
public string OfficeTypeId { get; set; }
}
I have a few scenarios where STD_OFFICETYPEID may come as LegacyOFFICETYPEID or OfficeID. Is there a way in which I can dynamically generate JsonPropertyName?
I am using System.Text.Json NuGet package.
Note that this example is simplified. In my production code there could be 20-25 concrete properties. And all of these properties could map to 5-10 different JsonPropertyNames each.
I'll tell you how I would do this: source generators.
First I would inject a new attribute, JsonPropertyNames(params string[] alternativeNames), and I'd decorate my class with it instead, giving it the full list of possible field names.
Then I'd have the source generator generate a second class matching properties with my original class, including properties for each of the alternative names provided using JsonPropertyNames. This is the class you'd be reading the Json into, and all your properties would get read in one of the properties.
Then the generator would add all the necessary AutoMapper mapping code to copy from my generated type to the original type, as well as a helper class that reads into the generated type and invokes AutoMapper to return the class for you.
So from the caller side, you'd just need to call one function to get your type, ignoring all the details behind the scenes.
if you are ready to swith to Newtonsoft.Json you can try this code
var json="{\"STD_USERTYPEID\":\"userId\",\"LegacyOFFICETYPEID\":\"officeId\"}";
FormField formField = DeserializeObj<FormField>(json);
public class FormField
{
[JsonProperty("STD_USERTYPEID")]
public string UserTypeId { get; set; }
[JsonPropertyNames(new string[] {"STD_OFFICETYPEID", "LegacyOFFICETYPEID" })]
public string OfficeTypeId { get; set; }
}
public T DeserializeObj<T>(string json) where T:new()
{
var jsonObj = JObject.FromObject(new T());
var attrs = GetAttrs<T>();
var jsonParsed = JObject.Parse(json);
foreach (var prop in jsonParsed.Properties())
{
var propName=prop.Name;
var attr=attrs.Where(a=>a.AttributeNames.Contains(propName)).FirstOrDefault();
if(attr!=null) jsonObj[attr.PropertyName]=prop.Value;
else jsonObj[propName]=prop.Value;
}
return jsonObj.ToObject<T>();
}
public static List<PropertyAttributes> GetAttrs<T>() where T: new()
{
var source= new T();
var attrs = new List<PropertyAttributes>();
foreach (PropertyInfo prop in source.GetType().GetProperties())
{
var attribute = prop.GetCustomAttribute<JsonPropertyNamesAttribute>();
if (attribute != null)
{
attrs.Add(new PropertyAttributes { PropertyName = prop.Name, AttributeNames = attribute.Names });
}
}
if (attrs.Count > 0) return attrs;
return null;
}
public class PropertyAttributes
{
public string PropertyName { get; set; }
public string[] AttributeNames { get; set; }
}
[AttributeUsage(AttributeTargets.All)]
public class JsonPropertyNamesAttribute : Attribute
{
private string[] names;
public JsonPropertyNamesAttribute(string[] names)
{
this.names = names;
}
public virtual string[] Names
{
get { return names; }
}
}
I'm pulling some data from an external API, and they have some objects defined by a discriminator string.
An example is an array of "Include" objects, where each one can have a different type of object as the Attributes parameter.
public class Include
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("attributes")]
public T Attributes { get; set; }
}
How can I define what object type T is, based on the Type parameter's value?
You could define Attributes as a JObject, then do .ToObject based on what the Type value is.
public class Include
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("attributes")]
public JObject Attributes { get; set; }
}
Then do an if (or switch) statement to handle each Type:
if (include.Type == "TypeOne")
ProcessTypeOne(include.Attributes.ToObject<TypeOne>());
else
...
That's assuming you have a class defined for each possible "Type". Or you can process them however you need to, just convert the Attributes to the necessary Type via the .ToObject<>() method.
You could do something like this:
using System;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
//setup
var a = new Include{Type = "Baz", Attributes = new Baz{Prop1 = "hello"}};
//convert
var b = Convert.ChangeType(a.Attributes, Type.GetType(a.Type));
//use
Console.WriteLine(b.Prop1);
}
}
public class Include
{
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("attributes")]
public dynamic Attributes { get; set; }
}
public class Baz
{
public string Prop1 { get; set; }
}
Convert.ChangeType would allow you to convert the dynamic object to whatever type it needed to be. However you may still find that you are in same place as #BraianM's answer and will have to type check so you know that a property or method is there.
You could also look at JsonConverter. Perhaps there is something there that would help.
Using YamlDotNet, I am attempting to deserialize the following YAML:
Collection:
- Type: TypeA
TypeAProperty: value1
- Type: TypeB
TypeBProperty: value2
The Type property is a required property for all objects under Collection. The rest of the properties are dependent on the type.
This is my ideal object model:
public class Document
{
public IEnumerable<IBaseObject> Collection { get; set; }
}
public interface IBaseObject
{
public string Type { get; }
}
public class TypeAClass : IBaseObject
{
public string Type { get; set; }
public string TypeAProperty { get; set; }
}
public class TypeBClass : IBaseObject
{
public string Type { get; set; }
public string TypeBProperty { get; set; }
}
Based on my reading, I think my best bet is to use a custom node deserializer, derived from INodeDeserializer. As a proof of concept, I can do this:
public class MyDeserializer : INodeDeserializer
{
public bool Deserialize(IParser parser, Type expectedType, Func<IParser, Type, object> nestedObjectDeserializer, out object value)
{
if (expectedType == typeof(IBaseObject))
{
Type type = typeof(TypeAClass);
value = nestedObjectDeserializer(parser, type);
return true;
}
value = null;
return false;
}
}
My issue now is how to dynamically determine the Type to choose before calling nestedObjectDeserializer.
When using JSON.Net, I was able to use a CustomCreationConverter, read the sub-JSON into a JObject, determine my type, then create a new JsonReader from the JObject and re-parse the object.
Is there a way I can read, roll-back, then re-read nestedObjectDeserializer?
Is there another object type I can call on nestedObjectDeserializer, then from that read the Type property, finally proceed through normal YamlDotNet parsing of the derived type?
It's not easy. Here is an GitHub issue explaining how to do polymorphic serialization using YamlDotNet.
A simple solution in your case is to to 2-step deserialization. First you deserialize into some intermediary form and than convert it to your models. That's relatively easy as you limit digging in the internals of YamlDotNet:
public class Step1Document
{
public List<Step1Element> Collection { get; set; }
public Document Upcast()
{
return new Document
{
Collection = Collection.Select(m => m.Upcast()).ToList()
};
}
}
public class Step1Element
{
// Fields from TypeA and TypeB
public string Type { get; set; }
public string TypeAProperty { get; set; }
public string TypeBProperty { get; set; }
internal IBaseObject Upcast()
{
if(Type == "TypeA")
{
return new TypeAClass
{
Type = Type,
TypeAProperty = TypeAProperty
};
}
if (Type == "TypeB")
{
return new TypeBClass
{
Type = Type,
TypeBProperty = TypeBProperty
};
}
throw new NotImplementedException(Type);
}
}
And that to deserialize:
var serializer = new DeserializerBuilder().Build();
var document = serializer.Deserialize<Step1Document>(data).Upcast();
Suppose you have this source model:
public abstract class SourceModelBase {
}
public class SourceContact : SourceModelBase {
public string FirstName { get; set; }
public string LastName { get; set; }
public KeyValuePair Pair { get; set; }
public SourceAddress Address { get; set; }
}
public class KeyValuePair { // Not derived from SourceModelBase.
public string Key { get; set; }
public string Value { get; set; }
}
public class SourceAddress : SourceModelBase {
public string StreetName { get; set; }
public string StreetNumber { get; set; }
}
Now the destination model should be mapped 1:1 by default (subject to normal AutoMapper configuration), but each thing derived from SourceModelBase should be mapped to a wrapper class class Wrap<T> { T Payload { get; set; } string Meta { get; set; } }.
public abstract class DestinationModelBase {
}
public class DestinationContact : DestinationModelBase {
public string FirstName { get; set; }
public string LastName { get; set; }
public KeyValuePair Pair { get; set; } // Not wrapped, base class not `SourceModelBase`.
public Wrap<DestinationAddress> Address { get; set; }
}
public class DestinationAddress : DestinationModelBase {
public string StreetName { get; set; }
public string StreetNumber { get; set; }
}
Since the contact class itself is derived from SourceModelBase it should be wrapped as well.
The result should have this structure:
Wrap<DestinationContact> Contact
string Meta // Comes from the custom wrapper logic.
DestinationContact Payload
string FirstName
string LastName
KeyValuePair Pair
string Key
string Value
Wrap<DestinationAddress> Address
string Meta // Comes from the custom wrapper logic.
DestinationAddress Payload
string StreetName
string StreetNumber
Obviously this wrapping should nest, illustrated by the fact that the mapped object itself is subject to it and so is its Address property.
For some reason all I keep finding are questions related to mapping from destination to source. I know I have to somehow use ResolveUsing and if the destination type is derived from SourceModelBase, somehow apply custom logic to provide the Wrap<T> value based on the value of the source property.
I don't know where to start at all, though. Especially when the source object itself is specified to be subject of the wrapping logic as well.
What's the best, most AutoMapper-idiomatic way to wrap the nested objects if they meet a condition and at the same time wrap the original object as well if it meets the same condition? I already have the mapper creation abstracted away so I can mold the original object automatically before passing it to the mapper, which may help with subjecting the original object to the resolver as well by doing mapper.Map(new { Root = originalObject }) so the resolver sees the instance of the original object as if it was a value of a property of source object as well, not the source object itself.
According to this issue on AutoMapper GitHub page, there is no direct way to do it.
But there is some workarounds. For example - reflection.
In this case you need to know wrapper type and implement converter for desired types. In this example it's MapAndWrapConverter from TSource to Wrap<TDestination>
CreateWrapMap method creates two bindings:
SourceAddress -> Wrap<DestinationAddress> and SourceContact -> Wrap<DestinationContact> which allow you to map SourceContant to wrapped DestinationContact.
internal class Program
{
public static void Main()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SourceAddress, DestinationAddress>();
cfg.CreateMap<SourceContact, DestinationContact>();
cfg.CreateWrapMap(
//func selecting types to wrap
type => typeof(DestinationModelBase).IsAssignableFrom(type)
&& !type.IsAbstract,
typeof(Wrap<>),
typeof(MapAndWrapConverter<,>));
});
var mapper = config.CreateMapper();
//Using AutoFixture to create complex object
var fixture = new Fixture();
var srcObj = fixture.Create<SourceContact>();
var dstObj = mapper.Map<Wrap<DestinationContact>>(srcObj);
}
}
public static class AutoMapperEx
{
public static IMapperConfigurationExpression CreateWrapMap(
this IMapperConfigurationExpression cfg,
Func<Type, bool> needWrap, Type wrapperGenericType,
Type converterGenericType)
{
var mapperConfiguration =
new MapperConfiguration((MapperConfigurationExpression)cfg);
var types = Assembly.GetExecutingAssembly().GetTypes();
foreach (var dstType in types.Where(needWrap))
{
var srcType = mapperConfiguration.GetAllTypeMaps()
.Single(map => map.DestinationType == dstType).SourceType;
var wrapperDstType = wrapperGenericType.MakeGenericType(dstType);
var converterType = converterGenericType.MakeGenericType(srcType, dstType);
cfg.CreateMap(srcType, wrapperDstType)
.ConvertUsing(converterType);
}
return cfg;
}
}
public class MapAndWrapConverter<TSource, TDestination>
: ITypeConverter<TSource, Wrap<TDestination>>
{
public Wrap<TDestination> Convert(
TSource source, Wrap<TDestination> destination, ResolutionContext context)
{
return new Wrap<TDestination>
{
Payload = context.Mapper.Map<TDestination>(source)
};
}
}
CreateWrapMap method is a little bit messy, especially the part with finding matching types. But it can be refined according to your needs.
Similar: How I can find Data Annotation attributes and their parameters using reflection
However, when attempting to gather the custom attribute, I always get back the same result. An empty ScriptIgnore.
PropertyInfo[] Properties = Entity.GetType().GetProperties();
foreach (PropertyInfo Property in Properties)
Upon debug, this line of code
var annotes = Property.GetCustomAttributes(typeof(ScriptIgnoreAttribute), false);
(I also tried using true)
looks like this
annotes | {System.Web.Script.Serialization.ScriptIgnoreAttribute[0]}
However, Property is defined as a class property like this
public virtual Lot Lot { get; set; }
There is no [ScriptIgnore] attribute attached. Moreover, when I have tried this on Property when it was defined like this
[ScriptIgnore]
public virtual ICollection<Lot> Lots { get; set; }
I get back the same result as above
annotes | {System.Web.Script.Serialization.ScriptIgnoreAttribute[0]}
How can I use reflection to determine if an attribute exists? Or other means if possible, I also tried
var attri = Property.Attributes;
but it did not contain any attributes.
The following code works:
using System.Web.Script.Serialization;
public class TestAttribute
{
[ScriptIgnore]
public string SomeProperty1 { get; set; }
public string SomeProperty2 { get; set; }
public string SomeProperty3 { get; set; }
[ScriptIgnore]
public string SomeProperty4 { get; set; }
}
Define a static extension:
public static class AttributeExtension
{
public static bool HasAttribute(this PropertyInfo target, Type attribType)
{
var attribs = target.GetCustomAttributes(attribType, false);
return attribs.Length > 0;
}
}
Put the following sample code into a method and it picks up the attribute correctly - including ICollection by the way:
var test = new TestAttribute();
var props = (typeof (TestAttribute)).GetProperties();
foreach (var p in props)
{
if (p.HasAttribute(typeof(ScriptIgnoreAttribute)))
{
Console.WriteLine("{0} : {1}", p.Name, attribs[0].ToString());
}
}
Console.ReadLine();
NOTE: if you're using EF dynamic proxy classes, I think you will need to use ObjectContext.GetObjectType() to resolve to the original class before you can get the attributes, since the EF-generated proxy class will not inherit the attributes.
var props = type.GetProperties()
.Where(p => p.GetCustomAttributes().Any(a => a is ScriptIgnoreAttribute))
.ToList();