protobuf-net: Handling nulls in IEnumerable? Via what attributes/settings? - c#

Some sample code below. The interesting/problem case is the Data property in
Mad. This code blows up (null value in the enumerable). Also, it works if i don't use the static attributes but instead the runtime type model, where i put in member.SupportNull = true for the fields (which is the behaviour i want), so what am i missing in the attributes / settings? Google search seems to indicate this is an open issue with probuf-net? That the same functionality is not available via attributes?
As as aside, if someone could suggest a way - i really love the runtime type model, i want to use that everywhere with a nice compiled model... but with it i lose the object versioning that protocol buffers solves! (via explicit tags). Is there any good way to maintain object version compatibility (simply adding fields) without doing all the static notation with fixed tags?
Basically the key thing with the runtime model is the assignment of tag indices and i can't think of a way of handling versions without explicitly specifying the tag indices via attributes...
[ProtoContract]
[ProtoInclude(1, typeof(ing))]
public class Eff
{
[ProtoMember(2)]
public string gg { get; set; }
}
[ProtoContract]
public class ing : Eff
{
[ProtoMember(1)]
public int zz { get; set; }
}
[ProtoContract]
public class Mad
{
[ProtoMember(1)]
public string Name { get; set; }
[ProtoMember(2)]
public IEnumerable<ing> Data { get; set; }
[ProtoMember(3)]
public ing Single { get; set; }
}
private static void Main(string[] args)
{
var obj = new Mad
{
Name = "test"
,Data = new[] { new ing {gg = "ooga", zz = -101},null,new ing()}
,Single = new ing {gg = "abc", zz = -999}
};
var m = new MemoryStream();
Serializer.Serialize(m, obj);
m.Seek(0, SeekOrigin.Begin);
var copy = Serializer.Deserialize<Mad>(m);
}

Short answer, it seems unavailable via attributes.
Workaround i'm doing for now - for every single type of interest(including the whole inheritance hierarchy) - add it to the type model yourself (with default handling so that it processes attributes), then call .GetFields() and set .SupportNull = true for each field (or only the relevant one)

Related

How to obtain value of property from applied attribute?

Is it possible to obtain the value from (lets say a string property) of a class from a custom attribute?
For example:
public class test
{
[EncodeHTML]
public string body { get; set; }
public int id { get; set; }
}
I would want the custom attribute EncodeHTML to be able to obtain the value of the setting value of the "body" property.
I know this can be achieved via the following:
public string body
{
get;
set {
value = HttpUtility.HTMLEncode(this);
}
But was wondering if this could be isolated for re-use across many class properties.
Here is a plain example of the custom attribute:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
sealed class EncodeHTMLAttribute : Attribute
{
public EncodeHTMLAttribute()
{
}
}
It is
var attr = typeof(test).GetProperty("body").GetCustomAttribute<EncodeHTMLAttribute>()
and you can access whatever you want.
To answer the question directly - no, the attributes are just metadata. They have no idea that a runtime instance of the decorated type even exist.
The closest you could do with an attribute is, at runtime, right when the app starts, find all types that have properties marked with that attribute and rewrite the setters code to what you want. It can be done in theory, but is hard, crazy, irresponsible and completely not recommended. Refer to this SO question to jump into the rabbit hole.
Having that said, to solve the underlying problem, you can just make a custom wrapper on a string.
public class HtmlEncodedString
{
public string Value { get; }
public HtmlEncodedString(string value) =>
Value = HttpUtility.HtmlEncode(value);
public static implicit operator string(HtmlEncodedString htmlEncodedString) =>
htmlEncodedString.Value;
public static implicit operator HtmlEncodedString(string value) =>
new HtmlEncodedString(value);
}
That's of course only a sketch. If you're using ASP.NET Core, consider implementing IHtmlContent. If you're allocating a lot of these, maybe making it a value type will decrease the pressure on the GC. Neverminding these details, you now can get reusability by just using this type instead of an attribute on a string.
public class Test
{
public HtmlEncodedString Body { get; set; }
public int Id { get; set; }
}
Because of the implicit operators, transition is seemless:
var test = new Test();
test.Body = "2 < 4";
string s = test.Body;
Console.WriteLine(s);
Console.WriteLine(test.Body);
> 2 < 4
> 2 < 4

Protobuf.net - all fields of a class required to be serialized

Here is an example class provided by Marc Gravel in his introduction on how to use Protobuf.net:
[ProtoContract]
class Person {
[ProtoMember(1)]
public int Id {get;set;}
[ProtoMember(2)]
public string Name {get;set;}
[ProtoMember(3)]
public Address Address {get;set;}
}
[ProtoContract]
class Address {
[ProtoMember(1)]
public string Line1 {get;set;}
[ProtoMember(2)]
public string Line2 {get;set;}
}
I have some questions, which I could not find the answers for after searching the web:
If on day 1, I know that I don't need the name property [ProtoMember(2)], then if I omit the [ProtoMember(2)] attribute, will Protobut.net ignore that property and not include it in the output serialized data? If true, then when the data is deserialized on the other end - what is Name initialized to - null?
Lets say that all 3 properties are initially serialized as shown above. If in the future, it turns out that the name property [ProtoMember(2)] is no longer required, can the [ProtoMember(2)] attribute be safely omitted so that only the first and third property are serialized? If true, would it be OK to simply leave the attribute numbers as shown (i.e. 1 & 3)? Any caveats if this is the case?
If it is OK to omit a serialization attribute for a property in a class, then what happens if the class definition on the deserialization side is out of sync? For example, say that the deserialization class defines all 3 properties above, but the serialization code only defines 1 and 3? Likewise, if the deserialization code only expects to see properties 1 and 3, but the serialization code sends all 3, would that still work or will it generate an error?
correct; in terms of what it is initialized to - that's usually up to your type, so in this case (since your type doesn't initialize it), yes: null (note: there is also an option in which the constructor can be suppressed, in which case it would be null even if your class had a constructor / initializer)
yes, that's fine (and expected)
the addition and removal of properties is expected and normal, so the library forgives either; when unexpected fields are received, what happens next depends on whether your class implements IExtensible (often by subclassing Extensible) - if it does, the unexpected data is stored separately, so that it can still be queried manually, or (more common) "round tripped" (i.e. if you serialize it again, the extra data is persisted, even though you didn't expect it)
(yes I know you didn't ask a 4) - the library also supports "conditional serialization", where-by properties can be omitted based on conditions - for example public bool ShouldSerializeName() => Name != null && SomethingElse == 42;
Since others may want to know the answer to these questions, I decided to post the question and share my findings.
These questions are actually quite easy to solve with a test program:
class Program
{
static void Main(string[] args)
{
var person = new Person1
{
Id = 12345,
Name = "Fred",
Address = new Address
{
Line1 = "Flat 1",
Line2 = "The Meadows"
}
};
//
byte[] arr = Serialize(person);
Person2 newPerson = Deserialize(arr);
/*
using (var file = File.Create("person.bin"))
{
Serializer.Serialize(file, person);
}
//
Person newPerson;
using (var file = File.OpenRead("person.bin"))
{
newPerson = Serializer.Deserialize<Person>(file);
}
*/
}
public static byte[] Serialize(Person1 person)
{
byte[] result;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, person);
result = stream.ToArray();
}
return result;
}
public static Person2 Deserialize(byte[] tData)
{
using (var ms = new MemoryStream(tData))
{
return Serializer.Deserialize<Person2>(ms);
}
}
}
[ProtoContract]
class Person1
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
[ProtoContract]
class Address
{
[ProtoMember(1)]
public string Line1 { get; set; }
[ProtoMember(2)]
public string Line2 { get; set; }
}
[ProtoContract]
class Person2
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public Address Address { get; set; }
}
So, to try out if Protobuf.net will ignore the Name property when serializing, first run the code in debug to see the total number of bytes with all 3 properties serialized. This can be done by setting a breakpoint at the Serialize(person) line and examining the size of arr.
Then, remove the [ProtoMember(2)] attribute from the Name property of the Person1 class. Running the code in debug shows that it leaves it out because the number of bytes is 6 less than before. When the object is then deserialized back to an object of Person2, it then shows that the Name property is initialized to null.
After replacing the [ProtoMember(2)] attribute for the Name property in the Person1 class, remove this same attribute for the Person2 class. After debugging through the code, it shows that after the Deserialize call, the Person2.Name property is set to null.
So, it looks like Protobuf.net was well designed to be quite flexible, efficient, and in some ways is backwards compatible in that it supports removing of obsolete properties.

C#, How to simply change order of class members

Hi I Have a class derived from another class . Like this :
public class Customer
{
public string Date{ get; set; }
public string InstallationNo{ get; set; }
public string SerialNo { get; set; }
}
Then I have created a class named Customer_U which derived from Customer
public class Customer_U:Customer
{
public string Bill{ get; set; }
}
I have simple question. I have google many time but no answer. I have list filled with data like:
List<Customer_U> customer= new List<Customer_U>() I create a excel using this list. In excel colum order is like this :
Bill --- Date --- InstalltionNo --- SerialNo.
Idont want this order. I want "Bill" member to be last columns . How to set order of member when createin a class derived from another class
There is no defined order in a CLR class; it is up to the implementation that is inspecting the metadata of the class to determine how a class is interpreted.
For example, your Excel library is probably using reflection to inspect the class you pass it and reflection makes no guarantees as to the order in which things are processed.
Other implementations such as the WCF DataContractSerializer or ProtoBuf.NET handle order through the use of DataMember.
That said, if your library can handle dynamic or anonymous types then you can use the approach detailed in the other answers. .NET seems to consistently reflect these types in the same order that they were created.
var x = new { Z = "", Y = 1, X = true };
Console.WriteLine(x.GetType().GetProperties().Select(y => y.Name));
However it should be noted that this is an implementation detail and should not be relied upon. I'd see if you library allows you to specify the mapping between properties in your class and columns in your spreadsheet otherwise you might run into weird bugs in the future!
There is no ordering of class members in .NET. Whenever someone iterates over members in a class, they are imposing some order themselves.
The default seems to be shallow-first. So in your case, first all of Customer_U members are enumerated, and then Customer's.
If you do the enumeration yourself, there's nothing easier than simply using your own enumeration method:
class A
{
public string Date { get; set; }
public string SerialNo { get; set; }
}
class B : A
{
public string Bill { get; set; }
public string InstallationNo { get; set; }
}
public static IEnumerable<PropertyInfo> GetProperties(Type type)
{
if (type.BaseType == typeof(object)) return type.GetProperties().OrderBy(i => i.Name);
return GetProperties(type.BaseType)
.Concat
(
type
.GetProperties
(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
.OrderBy(i => i.Name)
);
}
This simple recursive method will output
Date
SerialNo
Bill
InstallationNo
Deep-first, alphabetical. If you don't want the alphabetical sort, you can omit the OrderBys, but note that then the order is simply unspecified, not necessarily the order you used in your class.
You can use this when building your Excel, for example - if there's a way to impose an order in the output data. If there's no way to impose your own order in whatever you're using to output your data, you could do a mapping to a new object based on this data, and hope that it turns out well - however, doing this dynamically is actually quite a bit of work.
As the other answers have pointed out, there is no such thing as a defined order for class properties in .NET.
However, it seems that what you are looking for is not an ordering of the properties themselves, but in fact a way to sort the properties when serializing the objects, e.g. to Excel.
This IS easily implemented using classes from the System.Runtime.Serialization namespace. There are various classes there that could help you control the serialization process, and allow you to be as specific as you want.
The simplest solution would likely be simply applying the DataMember attribute:
[DataContract]
public class Customer
{
[DataMember(Order = 1)]
public string Date{ get; set; }
[DataMember(Order = 2)]
public string InstallationNo{ get; set; }
[DataMember(Order = 3)]
public string SerialNo { get; set; }
}
You can create a new anonymous class using linq:
var x = from costumerItem in YourList
select new { Date = costumerItem.Date, ...and so on };
Afterwards, move this class to the excel.
Create a wrapper list like
var reOrderedCustomer = Customer.select(a => new { a.Date, a.InstallationNo ,
a.SerialNo, a.Bill }).ToList()
Or do this in your first select method which fills Customer list (If you want to avoid anonymous type)

How do I map List<T1> to List<T2> using LINQ (or otherwise)

Lately I've worked on some programs that involve translating objects across various data domains. So I have a lot of mapping methods (sometimes as extension methods) for translating one type of object to another similar type in a different domain. Often, I also need a way to translate a List<> to a List<> of said types. This always involves having a method that simply creates a List<> of the target type, runs a foreach loop to add every element of the source List<> (but using the mapping method on each) and returning the new list. It's feeling pretty repetitive and like there might be something built into the language to do this (perhaps in LINQ?). I've looked at several similar issues involving List.ForEach() and the pros and cons of it (not what I'm looking for anyway). I'll illustrate with some example code below. Maybe there is no way to do what I want, and if that's the answer, then that's the answer, but I hope maybe there is. Please note, this is obviously just example code and comments about my overall program design won't really add anything because this is a very small dummy version of the problem at hand.
class A
{
public Guid Id { get; set; }
public string Email { get; set; }
public string MemberCode { get; set; }
}
class B
{
public string Email { get; set; }
public string MemberCode { get; set; }
// My custom mapping method
public A MapToA()
{
return new A()
{
Id = Guid.NewGuid(),
Email = this.Email,
MemberCode = this.MemberCode
};
}
// For list mapping, I have this, but I'd prefer
// to do something else that could utilize my custom mapper.
// Perhaps a built in LINQ method?
public static List<A> MapToListOfA(List<B> listOfB)
{
List<A> listOfA = new List<A>();
foreach (var b in listOfB)
{
listOfA.Add(b.MapToA());
}
return listOfA;
}
}
// Class C shows what I currently do that I'd like to get
// away from:
class C
{
public List<A> ListOfA { get; set; }
// other properties unrelated to the problem
// This is how I might use the MapToListOfA method,
// but I'd rather have something better.
public C(List<B> listOfB)
{
this.ListOfA = B.MapToListOfA(listOfB);
}
}
// I'd like something more like this:
class D
{
public List<A> ListOfA { get; set; }
// other properties unrelated to the problem
public D(List<B> listOfB)
{
// This doesn't compile, of course, but I hope
// it illustrates what I'm intending to do:
this.ListOfA = listOfB.Select(b => b.MapToA());
}
}
// This doesn't compile, of course, but I hope
// it illustrates what I'm intending to do:
this.ListOfA = listOfB.Select(b => b.MapToA());
It doesn't compile because listOfB.Select(b => b.MapToA()) produces an instance of IEnumerable<A> which is not assignable to List<A>.
Use ToList and it should compile fine
this.ListOfA = listOfB.Select(b => b.MapToA()).ToList();

Not marked as serializable error when serializing a class

I am serializing an structure by using BinaryFormatter using this code:
private void SerializeObject(string filename, SerializableStructure objectToSerialize)
{
Stream stream = File.Open(filename, FileMode.Create);
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, objectToSerialize);
stream.Close();
}
Which objectToSerialize is my structure, I'm calling this function like this:
SerializableStructure s = new SerializableStructure();
s.NN = NN;
s.SubNNs = SubNNs;
s.inputs = inputs;
SerializeObject(Application.StartupPath + "\\Save\\" + txtSave.Text + ".bin", s);
Which SerializableStructure, and Type of NN, SubNNs and inputs are serializable. (inputs contains some Points, Rectangles and generic lists).
Now, When I run my code, I am given this error:
Type 'MainProject.Main' in Assembly 'MainProject, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.
Why I'm given this error? Main is my form, and these variables are located in my form.
I have successfully serialized Type of NN with MemoryStream and VB.NET , But I don't know why I'm getting this error?
Here is the definition of my structures:
SerializableStructure:
[Serializable()]
public class SerializableStructure
{
public List<Inputs> inputs = new List<Inputs>();
public NeuralNetwork NN;
public NeuralNetwork[] SubNNs;
}
Inputs:
[Serializable()]
public class Inputs
{
public string XPath { get; set; }
public string YPath { get; set; }
public string ImagePath { get; set; }
public string CharName { get; set; }
public string CharBaseName { get; set; }
public List<double> x { get; set; }
public List<double> y { get; set; }
public List<double> DotsX { get; set; }
public List<double> DotsY { get; set; }
public List<Point> GravityCenters { get; set; }
public List<Rectangle> Bounds { get; set; }
public override string ToString()
{
return CharName;
}
public Inputs(string xPath, string yPath, string imagePath, string charName, string charBaseName)
{
XPath = xPath;
YPath = yPath;
ImagePath = imagePath;
CharName = charName;
CharBaseName = charBaseName;
x = new List<double>();
y = new List<double>();
GravityCenters = new List<Point>();
Bounds = new List<Rectangle>();
}
}
Also NN is very big structure(!).
This almost alwas means you have an event (or other delegate - maybe a callback) somewhere in your object model, that is trying to be serialized. Add [NonSerialized] to any event-backing fields. If you are using a field-like event (the most likely kind), this is:
[field:NonSerialized]
public event SomeDelegateType SomeEventName;
Alternatively: most other serializers don't look at events/delegates, and provide better version-compatibility. Switching to XmlSerializer, JavaScriptSerializer, DataContractSerializer or protobuf-net (just 4 examples) would also solve this by the simple approach of not trying to do this (you almost never intend for events to be considered as part of a DTO).
The problem is that you are trying to serialize a class derived from Form. The Form class is fundamentally unserializable. It has an enormous amount of internal state that is highly runtime dependent. That starts with an obvious property like Handle, a value that's always different. Less obvious are properties like Size, dependent on user preferences like the size of the font for the window caption. Ends with all the text, location and sizes for the controls, they are subject to localization. The odds that a serialized Form object can be properly deserialized anywhere at any time to create an exact clone of the form are zero.
Microsoft made no bones about it when they wrote the code, they simply omitted the [Serializable] attribute from the class declaration. Which is why you get the exception.
You'll have to aim lower, write your own class to capture your form's state. And give it the attribute. You'll need to write a bunch of code that maps between the form and control properties to an object of that class, back and forth.

Categories

Resources