I use surrogates and I'd like to perform a check to skip wrong items during the serialization process, but I can't find a way to do it, any idea?
BeforeSerialize method is called after the creation of the surrogate and I'd like to know how to skip items that have no the requirements specified in the protoBuf serialization context.
Here following, a sample code to reproduce my scenario.
public class Person
{
public Person(string name, GenderType gender)
{
Name = name;
Gender = gender;
}
public string Name { get; set; }
public GenderType Gender { get; set; }
}
public class PersonSurrogate
{
public string Name { get; set; }
public byte Gender { get; set; }
public PersonSurrogate(string name, byte gender)
{
Name = name;
Gender = gender;
}
protected virtual bool CheckSurrogateData(GenderType gender)
{
return gender == GenderType.Both || (GenderType)Gender == gender;
}
#region Static Methods
public static implicit operator Person(PersonSurrogate surrogate)
{
if (surrogate == null) return null;
return new Person(surrogate.Name, (GenderType)surrogate.Gender);
}
public static implicit operator PersonSurrogate(Person source)
{
return source == null ? null : new PersonSurrogate(source.Name, (byte)source.Gender);
}
#endregion
protected virtual void BeforeSerialize(ProtoBuf.SerializationContext serializationContext)
{
var serializer = serializationContext.Context as FamilySerializer;
if (serializer == null)
throw new ArgumentException("Serialization context does not contain a valid Serializer object.");
if (!CheckSurrogateData(serializer.GenderToInclude))
{
// ** How can I exclude this item from the serialization ? **//
}
}
}
[Flags]
public enum GenderType : byte
{
Male = 1,
Female = 2,
Both = Male | Female
}
/// <summary>
/// Class with model for protobuf serialization
/// </summary>
public class FamilySerializer
{
public GenderType GenderToInclude;
public RuntimeTypeModel Model { get; protected set; }
protected virtual void FillModel()
{
Model = RuntimeTypeModel.Create();
Model.Add(typeof(Family), false)
.SetSurrogate(typeof(FamilySurrogate));
Model[typeof(FamilySurrogate)]
.Add(1, "People") // This is a list of Person of course
.UseConstructor = false;
Model.Add(typeof(Person), false)
.SetSurrogate(typeof(PersonSurrogate));
MetaType mt = Model[typeof(PersonSurrogate)]
.Add(1, "Name")
.Add(2, "Gender");
mt.SetCallbacks("BeforeSerialize", null, null, null); // I'd like to check surrogate data and skip some items - how can I do?
mt.UseConstructor = false; // Avoids to use the parameterless constructor.
}
}
What you describe is not a scenario that the serializer currently makes any attempt to target. Conditional serialization is supported on a per-property/field basis, but not on a per object basis.
There might still be ways to get it to work, though - but it depends on what the context is, i.e. what is it that has a Person object in your model? And can you change that model at all? (possibly not, since you're using surrogates).
My default answer, as soon as things stop working cleanly, is to say: create a separate DTO model for your serialization work, and populate that with the data that you intend to serialize - rather than fighting to get your regular domain model to play nicely with complex serialization requirements.
Related
I'm digging into an object which contains "values" in a string format that correspond to objects and properties nested within the object I get said values from. I.E. If my object contains a list of nested objects that contains, say.. Name, within a Contact object, then I have a "value" that might read something like "ContactInfo[0].Name".
How do I verify the property exists? I'm pretty sure that if I can figure this first part out, then I can worry about getting the value with a lot less difficulty.
I've tried using something along the lines of:
public static bool HasProperty(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
with the arguments "MasterInfo", "Keys[0].KeyWord" and "Keys[1].Keyword" respectively.
I've tried breaking them down by DOT notation into segements and everything else I can think of, but I can't seem to use the keywords in the method successfully. Always False.
I tried running the MasterInfo object through a routine that broke the keyword down by '.' and iterated over each portion, but no change in the result... always False.
Fresh eyes are so very welcome! I've got to be missing something simple and/or right in front of me...
// simplified object example
public class MasterInfo
{
public int Id { get; set; }
public List<ContactInfo> Contacts {get; set;}
public Message MessageDetail { get; set; }
public List<Key> Keys { get; set; }
}
public class ContactInfo
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class Message
{
public int Id { get; set; }
public string MessageContent { get; set; }
}
public class Key
{
public int Id { get; set; }
public string KeyWord { get; set; }
}
// serialized JSON of what the MasterInfo class would be populated with, easier to read than with '.' notation
{
"Id" : 1,
"Contacts" : [
{
"Id" : 1,
"Name" : "Beavis",
"Email" : "beavis#asd.asd"
}
],
"MessageDetail" : {
"Id" : 23,
"MessageContent" : "Hello, %%Contacts[0].Name%%, this was sent to you at %%Contacts[0].Email%%"
},
"Keys" : [
{
"Id" : 1,
"KeyWord" : "Contacts[0].Name"
},
{
"Id" : 2,
"KeyWord" : "Contacts[0].Email"
}
]
}
// method I'm trying to use to verify the keyword (property) exists before attempting to get it's value...
public static bool HasProperty(this object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName) != null;
}
So far, everything turns up false when evaluated. I'm pretty sure this has to do with the fact that I'm diving into nested objects, but at this point I'm not certain of anything.
public static bool HasProperty(this object obj, params string[] properties)
{
return HasProperty(obj.GetType(), properties);
}
public static bool HasProperty(this Type type, params string[] properties)
{
if (properties.Length == 0) // if done properly, shouldn't need this
return false;
var propertyInfo = type.GetProperty(properties[0]);
if (propertyInfo != null)
{
if (properties.Length == 1)
return true;
else // need to check the next level...
{
Type innerType = propertyInfo.PropertyType.GetGenericArguments().FirstOrDefault();
if (innerType != null)
return HasProperty(innerType, properties.Skip(1).ToArray());
else
return false;
}
}
else
return false;
}
public static void Testing()
{
MasterInfo masterInfo = new MasterInfo();
Console.WriteLine(HasProperty(masterInfo, "Id")); // true
Console.WriteLine(HasProperty(masterInfo, "Contacts", "Name")); // true
Console.WriteLine(HasProperty(masterInfo, "Contacts", "Address")); // false
}
Having the following classes:
public class DeviceParameter
{
public string Key { get; set; }
public Guid DeviceId { get; set; }
public string Value { get; set; }
}
A device can have a lot of parameters, of different types, but they are all stored in the database as strings.
public abstract class DeviceValueTypedParameter<TValue>
{
public string CodeName { get; }
public TValue Value { get; set; }
public Guid DeviceId { get; set; }
public DeviceValueTypedParameter(string codeName)
{
this.CodeName = codeName;
}
}
DeviceValueTypedParameter is an abstraction, to have a typed value (TValue) used on C# of the parameter value, instead of using the string that we get from the database. There is no heritance between DeviceValueTypedDeviceParameter and DeviceParameter because I want to make the conversion from TValue to string by composition.
public class ArmingStatusParameter : DeviceValueTypedParameter<ArmingStatuses>
{
public const string CODE_NAME = "ArmingStatus";
public ArmingStatusParameter() : base(CODE_NAME)
{
}
}
public enum ArmingStatuses
{
Unknown,
Armed,
Disarmed,
}
ArmingStatusParameter is an example of a typed Parameter that can exist, where the value is an Enum of ArmingStatuses. Other types that can exist are DateTimes, int32, double, etc.
I've already accomplished the conversion from Typed value to string, but now I'm struggling how to properly do the conversion from string to Typed value.
Tried different approaches:
Implicit or Explicit conversion
Extension method
Converter classes for each type that exists
Generic converter class based on TValue type
Option 1: is easy to implement, but violates the POCO of
ArmingStatusParameter. People can forget to implement the implicit/explicit operators and errors will only happen at compile time.
Option 2: violates the Interface segregation principle (ISP), since is needed to access directly the conversion.
Option 3: it works, but people will have to create a lot of classes and the code will be too verbose. For each different parameter, is needed to instance a new {X}TypedParameterConverter.
Option 4: seems the best option, but I am having troubles "in making it work"
I was thinking about something like this:
public interface IDeviceValueTypedParameterConverter
{
bool TryConvert<T, TValue>(DeviceParameter deviceParameter,
DeviceValueTypedParameter<TValue> deviceValueTypedParameter)
where T : DeviceValueTypedParameter<TValue>;
}
public class DeviceValueTypedParameterConverter : IDeviceValueTypedParameterConverter
{
public bool TryConvert<T, TValue>(DeviceParameter inputParameter,
DeviceValueTypedParameter<TValue> outputParameter)
where T : DeviceValueTypedParameter<TValue>
{
bool result = true;
if (inputParameter == null)
{
throw new NullReferenceException($"DeviceValueTypedParameter:'{typeof(T)}' must be initialized first");
}
if (inputParameter.Value is int)
{
result = int.TryParse(inputParameter.Value, out int temp);
outputParameter.Value = (TValue)temp;
}
else if (inputParameter.Value is Enum)
{
// some other code to convert the Enum's
}
// more else ifs one for each type
// (...)
else
{
result = false;
}
outputParameter.DeviceId = inputParameter.DeviceId;
return result;
}
}
Issues:
All the Ifs gives me a warning saying: "The given expression is never of the provided".
Can't make the cast (TValue). It says can't convert int to TValue. The only solution is creating value via reflection?
Here is my attempt to make this work - I am not sure if it violates some details you did not explain (or did explain). Since out parameters can't use polymorphism, I created an interface to represent the common functions across the typed parameter base class. Since there are no static virtual methods, I used object methods and created a result object that will is used if the conversion is possible.
I see no reason for the conversion method to have multiple instances or need an interface, so I created it as a single static method. I used an enum to capture the type of conversion needed for the parameter accessible from the passed in type, and had to do a tricky conversion through object to handle assignment to the out parameter value field, since C# has no type switching capability for assignments. Note this could cause a runtime error if the IsPossible method doesn't properly filter all cases and the ChangeType fails.
public enum ValueParseTypes {
Enum,
DateTime,
Int
}
public interface IDeviceValueTypedDeviceParameter<TValue> {
string CodeName { get; }
TValue Value { get; set; }
Guid DeviceId { get; set; }
ValueParseTypes ParseType { get; set; }
bool IsPossibleValue(DeviceParameter aValue);
}
public abstract class DeviceValueTypedDeviceParameter<TValue> : IDeviceValueTypedDeviceParameter<TValue> {
public string CodeName { get; }
public TValue Value { get; set; }
public Guid DeviceId { get; set; }
public ValueParseTypes ParseType { get; set; }
public DeviceValueTypedDeviceParameter(string codeName, ValueParseTypes parseType) {
this.CodeName = codeName;
this.ParseType = parseType;
}
public virtual bool IsPossibleValue(DeviceParameter aValue) => false;
}
public class ArmingStatusParameter : DeviceValueTypedDeviceParameter<ArmingStatuses> {
public const string CODE_NAME = "ArmingStatus";
public ArmingStatusParameter() : base(CODE_NAME, ValueParseTypes.Enum) {
}
static HashSet<string> ArmingStatusesNames = Enum.GetNames(typeof(ArmingStatuses)).ToHashSet();
public override bool IsPossibleValue(DeviceParameter aValue) => ArmingStatusesNames.Contains(aValue.Value);
}
public enum ArmingStatuses {
Unknown,
Armed,
Disarmed,
}
public class PoweredOnStatusParameter : DeviceValueTypedDeviceParameter<DateTime> {
public const string CODE_NAME = "PoweredOn";
public PoweredOnStatusParameter() : base(CODE_NAME, ValueParseTypes.DateTime) {
}
public override bool IsPossibleValue(DeviceParameter aValue) => DateTime.TryParse(aValue.Value, out _);
}
public class VoltageStatusParameter : DeviceValueTypedDeviceParameter<int> {
public const string CODE_NAME = "PoweredOn";
public VoltageStatusParameter() : base(CODE_NAME, ValueParseTypes.Int) {
}
public override bool IsPossibleValue(DeviceParameter aValue) => Int32.TryParse(aValue.Value, out _);
}
public static class DeviceValueTypedParameterConverter {
public static bool TryConvert<TValue>(DeviceParameter inputParameter, IDeviceValueTypedDeviceParameter<TValue> outputParameter)
where TValue : struct {
if (inputParameter == null)
throw new ArgumentNullException(nameof(inputParameter));
else if (outputParameter == null)
throw new ArgumentNullException(nameof(outputParameter));
bool result = false;
if (outputParameter.IsPossibleValue(inputParameter)) {
outputParameter.DeviceId = inputParameter.DeviceId;
switch (outputParameter.ParseType) {
case ValueParseTypes.Enum:
if (Enum.TryParse(inputParameter.Value, out TValue typedValue)) {
outputParameter.Value = typedValue;
result = true;
}
break;
case ValueParseTypes.DateTime:
if (DateTime.TryParse(inputParameter.Value, out var dtValue)) {
outputParameter.Value = (TValue)Convert.ChangeType(dtValue, typeof(TValue));
result = true;
}
break;
case ValueParseTypes.Int:
if (Int32.TryParse(inputParameter.Value, out var intValue)) {
outputParameter.Value = (TValue)Convert.ChangeType(intValue, typeof(TValue));
result = true;
}
break;
}
}
return result;
}
}
Now you can use it like so:
var as_tv = new DeviceParameter() {
Key = "testkey",
DeviceId = new Guid(),
Value = "Armed"
};
var asp = new ArmingStatusParameter();
if (DeviceValueTypedParameterConverter.TryConvert<ArmingStatuses>(as_tv, asp)) {
// work with asp
}
var po_tv = new DeviceParameter() {
Key = "testkey2",
DeviceId = new Guid(),
Value = "4/15/2019 17:36"
};
var pop = new PoweredOnStatusParameter();
if (DeviceValueTypedParameterConverter.TryConvert<DateTime>(po_tv, pop)) {
// work with pop
}
var v_tv = new DeviceParameter() {
Key = "testkey3",
DeviceId = new Guid(),
Value = "17"
};
var vp = new VoltageStatusParameter();
if (DeviceValueTypedParameterConverter.TryConvert<int>(v_tv, vp)) {
// work with vp
}
I'm trying to build a series of attribute classes to make it easier for our development team to validate objects. The objects are POCO classes like this.
public class User
{
public string Name { get; set; }
public string Company { get; set; }
}
I want to decorate this model with a custom attribute.
public class User
{
[MustHaveValue]
public string Name { get; set; }
public string Company { get; set; }
}
Then I would create my own class implementing ValidationAttribute, the base class in .NET Framework, which belongs to System.ComponentModel.DataAnnotations.
public class MustHaveValueAttribute : ValidationAttribute
{
.
.
public override IsValid(object value)
{
// validation logic.
}
}
And then I can validate the User model whenever I want by making the set of instances like ValidationContext, List<ValidationResult>.
But in an enterprise environment, problems just can't be solved by a specific class. My validation scenario requires more complex and more flexible ways. Imagine that one of the required validation scenarios would something like this.
public class User
{
public string Name { get; set; }
public string Company { get; set; }
// Check if an item exists in this list.
[MustHaveMoreThanOneItem]
public IList<Client> Clients { get; set; }
}
Then I would need to make another attribute class
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
.
.
public override IsValid(object value)
{
// Let's assume this value is List<Client> for now.
// I know the exact type, so I'm going to cast it to List<Client> without further considerations
List<Client> clients = value as List<Client>;
if(clients.Count > 0) {
return true;
} else {
return false;
}
}
}
But the problem is that there are a lot of other models that have a nested list items. Try to imagine the time when I want to reuse the MustHaveMoreThanOneItem in one of the other models like...
public class Department
{
public string Name { get; set; }
[MustHaveMoreThanOneItem]
public IList<Employee> { get; set; }
}
You already know that it's not going to work because it was strongly typed only for List<Client>. So I decided to use Generic there to solve this problem.
But to my disappointment, the _Attribute interface doesn't support Generic. There's no additional implementation like _Attribute<T> : Attribute and therefore, no ValidationAttribute<T> alas!! I just cannot use Generic here !!
public class Department
{
public string Name { get; set; }
// No way to use this syntax.
[MustHaveMoreThanOneItem<Employee>]
public IList<Employee> { get; set; }
}
So I made a conclusion that Attribute must have been designed for a fixed set of validations like email format, card format, null check, and etc IMAO.
But I still want to use an attribute and give a lot of flexibilities in it to prevent the duplicated, verbose validation codes like this.
if(model.Clients.Count > 0) ...
if(model.Name != null) ...
if(model.Clients.GroupBy(x => x.Country == Country.USA).Count >= 1) ...
if(model.Clients.Where(x => x.CompanyName == Company.Google).ToList().Count > 1 ) ...
.
.
.
I want to pose two questions here.
If Attirbute supports Generic, this problem will be solved?
Is there any way to implement Generic Attribute? in order to use
[MustHaveMoreThanOneItem<Employee>] annotation on a class member?
You can generically check any object that implements IEnumerable like this:
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
// omitted null checking
var enumerable = value as IEnumerable;
var enumerator = enumerable.GetEnumerator();
if (!enumerator.MoveNext())
{
return false;
}
if (!enumerator.MoveNext())
{
return false;
}
return true;
}
}
C# by definition does not support generic type attributes, although this has been requested actively for a long time:
https://github.com/dotnet/roslyn/issues/953
https://github.com/dotnet/csharplang/issues/124
However, you can still inject a type into a validation attribute via constructor. You then can use reflection or whatever you need to define your custom validation criteria.
public class MustHaveMoreThanOneItemAttribute : ValidationAttribute
{
public Type EnumerableType { get; }
public MustHaveMoreThanOneItemAttribute(Type t)
=> this.EnumerableType = typeof(ICollection<>).MakeGenericType(t);
public override bool IsValid(object value)
{
var count = this.EnumerableType.GetProperty("Count").GetValue(value) as int?;
return (count ?? 0) > 1;
}
}
Now this allows you to use something similar to your goal:
public class Department
{
public string Name { get; set; }
[MustHaveMoreThanOneItem(typeof(Employee))]
public IList<Employee> { get; set; }
}
I have a Pharmacy class which contains lots of properties, and a Pharmacy is declared as unique by having the ID property as a key.
I have code in place which fetches all rows in a table from MySQL back to Pharmacy objects with their properties.
I want compare two List<Pharmacy> objects for the entries in them and check if the same ID exists in both tables, if it doesn't exist then add it to a new List<Pharmacy. If the ID exists in both but the data in the objects differs, then save that object to a new List<Pharmacy aswell.
This is how the class looks like.
public class Pharmacy
{
[Key]
public string Tunniste { get; set; }
public string Lyhenne { get; set; }
public string PitkaNimi { get; set; }
public string YlempiYksikko { get; set; }
public string Hierarkiataso { get; set; }
public string VoimassaoloAlkaa { get; set; }
public string VoimassaoloPaattyy { get; set; }
...
}
It's in Finnish but I hope you can live with that.
Here's how I've tried to check if they're identical.
for (int i = 0; i != pharmacyListFromArchive.Count; i++)
{
if (pharmacyListFromArchive[i].Equals(pharmacyListFromNew[i]))
{
Console.WriteLine("Objects are identical.");
}
else
{
Console.WriteLine("Objects are NOT identical. {0} - {1}", pharmacyListFromArchive[i].Tunniste, pharmacyListFromNew[i].Tunniste);
}
}
But when I run that, none of the objects register as identical even though they are identical in data. How can I work around this?
The standard implementation of Equals checks only for reference equality. What is the default behavior of Equals Method?
You can either override the behavior of Equals. Guidelines for Overriding Equals() and Operator == (C# Programming Guide).
public class Pharmacy {
// fields ...
public override bool Equals(object obj) {
// If parameter is null return false.
if (obj == null) {
return false;
}
// If parameter cannot be cast to Pharmacy return false.
Pharmacy p = obj as Pharmacy;
if ((System.Object)p == null) {
return false;
}
// Return true if the fields match:
return p.Lyhenne == this.Lyhenne &&
p.PitkaNimi == this.PitkaNimi
// && etc...
;
}
public override int GetHashCode() {
return Lyhenne.GetHashCode() ^ PitkaNimi.GetHashCode() /* ^ etc ... */;
}
}
Or you implement a custom IEqualityComparer IEqualityComparer Interface. This might be preferable if your ORM Mapper relies on the default equals (like Entity Framework does).
version : protobuf-net r282
Serialize XElement object and deserialize it will result lost of relationship information such like NextNode, Parent...
It looks like that only Xml data in it is stored.
Is there any way to store relationship information?
Thanks!
Here is a class I have used to test:
[ProtoContract]
public class Person
{
[ProtoMember(1)]
public string FirstName { get; set; }
[ProtoMember(2)]
public string FamilyName { get; set; }
[ProtoMember(3)]
public int Age { get; set; }
[ProtoMember(4)]
public XDocument Details { get; set; }
[ProtoMember(5)]
public XElement Business { get; set; }
// ctor
public Person() { } // ctor for Deserialize
public Person(string first, string family, int age, XDocument details)
{
FirstName = first;
FamilyName = family;
Age = age;
Details = details;
Business = Details == null ? null : Details.Descendants("business").FirstOrDefault();
}
// calculated properties
public string FullName { get { return FirstName + " " + FamilyName; } }
// Methods
public string GetDetails(string key)
{
if (this.Details == null) return null;
var found = (from n in Details.Descendants(key)
select n.Value).FirstOrDefault();
return found;
}
}
[Update]
One way to avoid the problem is serialize absolute path of xelement instead of itself.
Here is a sample.
using System.Xml.XPath;
.....
//[ProtoMember(5)]
public XElement Business { get; set; }
[ProtoMember(5)]
public string BusinessSerialized
{
get { return Business == null ? null : Business.GetAbsoluteXPath(); }
set
{
if (value == null) { Business = null; }
else
{
Business = Details.XPathSelectElements(value).FirstOrDefault();
}
}
}
GetAbsoluteXPath is a extension method for XElement. I have found it in this question.
Wow, I wouldn't even have expected that XElement worked at all - I guess it is finding the .ToString() / .Parse() pair, and using those.
This simply isn't a use-case that protobuf-net targets. And I certainly wouldn't expect it to retain data outside of the immediate leaf/node that is being represented in the data (that would basically mean that it had to serialize an entire XDocument / whatever every time it saw any element - via the .Document property.
Your workaround with storing the absolute xpath is a reasonable one; that is very different to storing an XElement.
The way to obtain relationship information of XmlNode is updated to post. Though it gives a good result, it's a little bit slow.