Can I dynamically apply XmlIgnore to properties of an ArrayItem? - c#

I am trying to serialize a List<T> where T: EntityObject and would love to leave out all the EntityKey and other EntityReference properties from the items in the list. Can this be done dynamically possibly using XmlAttributeOverrides?
As far as I can see, the XmlAttributeOverrides options only really point to the top level object ie the List<T> and not the T themselves, which is not very helpful to me.
Could anyone point me to a way to dynamically ignore properties of ArrayItems?
Here is a simple example I have been using that does not use EntityObjects but it should illustrate what I would like to do:
public class Car
{
public String Make { get; set; }
public String Model { get; set; }
public Double EngineSize { get; set; }
}
[Test]
public void WouldLoveToDynamicallyLeaveOutMembersOfArrayItems()
{
var cars = new List<Car>
{
new Car
{
Make = "Ferrari",
Model = "F1",
EngineSize = 6000
},
new Car
{
Make = "Williams",
Model = "F1",
EngineSize = 5500
}
};
var attributeOverrides = new XmlAttributeOverrides();
attributeOverrides.Add(typeof(Double), "EngineSize", new XmlAttributes {XmlIgnore = true});
var xs = new XmlSerializer(cars.GetType(), attributeOverrides, new []{ typeof(Car) }, new XmlRootAttribute("cars"), "");
var ms = new MemoryStream();
xs.Serialize(ms, cars);
ms.Position = 0;
var sr = new StreamReader(ms);
var result = sr.ReadToEnd();
Assert.IsFalse(result.Contains("EngineSize"));
}

Yes you can do that - the main error is you need typeof(Car) instead of typeof(double). With this, note that XmlAttributeOverrides does not just apply to the top-level onject.
However, I'm not sure that is the easiest route. Firstly, note that you must store and re-use the serialiser when using XmlAttributeOverrides otherwise you will leak assemblies.
I expect the main reason you want to do this is because you don't want to edit he generated file. However, there is another way; in a separate file, in the right namespace, you can use:
partial class Car {
public bool ShouldSerializeEngineSize() { return false; }
}
Where "ShouldSerialize*" is a pattern recognised and used by XmlSerializer to control conditional serialization. The "partial" class is simply a way of combining two separate code files into a single type (and was designed for this generated-content scenario).
This way, you dont need to mess with XmlAttributeOverrides, and you can use the simpler "new XmlSeralizer(Type)" (which has automatic caching per-type).

Related

Ensuring anonymous objects in C# all have certain common properties

I have a problem regarding anonymous objects in C#. The situation is as follows:
I have a C# web app which does NOT use the traditional ASP.NET Razor engine, but instead uses the RazorEngine open source project (https://github.com/Antaris/RazorEngine). I'm not sure if this is relevant, but it could be.
I'm passing a model object to each page I'm displaying. Each model object is different, there are many pages and therefore many different model objects, but I would rather not have to declare separate classes for each model, which is why I've been using anonymous classes:
// In method which displays page A:
var model = new {
Lang = _lang,
PropX = "foo",
PropY = "bar"
};
RazorEngine.Run("templateA", model);
// In a different method which displays page B:
var model = new {
Lang = _lang,
PropZ = "smu"
};
RazorEngine.Run("templateB", model);
You may notice that botn (and in fact, all) those models have a common property (the "Lang" property), a few common properties actually (Lang is the only one displayed in the example above to simplify matters).
My main problem is that I'm trying to ensure that those properties are added to all the models in a way which guarantees that they are included to all pages, and if I later decide to add a new common property, then I can do that in a single place.
One way would of course be to drop the anonymous classes, and use typed classes which all inherit from a single base class, which would declare the common properties. But this would be a lot of boilerplate code, and if there is another solution then I would prefer that.
Another solution would be to either declare the common properties in a sub-property of the model object, or declare the individual page properties in a sub object:
// Either like this:
var model = new {
Common = GetCommonModelProperties(),
PropX = "foo",
PropY = "bar"
};
public object GetCommonModelProperties()
{
return new {
Lang = _lang
};
}
// etc.
// or like this:
var pageModel = new {
PropX = "foo",
PropY = "bar
};
var model = CreateModel(pageModel);
RazorEngine.Run("templateA", model);
// where CreateModel could be implemented like this:
public object CreateModel(object pageModel)
{
return new
{
Lang = _lang,
// etc., whatever common properties there exist
Data = pageModel
};
}
The problem with this approach is that I would have to modify all my templates, either all instances where those pages refer to the common property (I would have to rename all Model.Lang instances to Model.Common.Lang), or to the individual page data (modify Model.AnyProperty to Model.Data.AnyProperty). Of course there is a great risk of errors when such a rewrite takes place.
So: is there a way to create an anonymous object, where a number of its properties are always the same, but the rest can be specified dynamically?
I've tried to create two separate objects, and then combine them into one, using code from this question: Merging anonymous types
var commonModel = new {
Lang = _lang
};
var pageModel = new {
PropX = "foo",
PropY = "bar"
};
var model = Merge(commonModel, pageModel);
and yes, this works. Until I have to use the Lang object, which is of a class type (which I have full control over), and this class overloads operator[]. If I use this workaround, the overload stops working, and I get the error:
Cannot apply indexing with [] to an expression of type 'object'
N.b. the indexing works perfectly fine if I just include the Lang property in a regular anonymous object.
I've also tried to create a separate base class for all the models, declare all the common properties in that class, but also derive it from System.Dynamic.DynamicObject, and override the TryGetMember method which would dynamically look up the page properties from a dictionary (which would work, since those properties are usually simple objects, i.e. they don't override the indexing operator, so I can add those properties dynamically at runtime:
var pageModel = new {
PropX = "foo",
PropY = "bar"
};
var model = CreateMainModel(pageModel);
public object CreateMainModel(object pageModel)
{
var mainModel = new BaseModel()
mainModel.Lang = _lang;
foreach (System.Reflection.PropertyInfo fi in pageModel.GetType().GetProperties())
{
mainModel.PageProperties[fi.Name] = fi.GetValue(pageModel, null);
}
return mainModel;
}
class BaseModel : DynamicObject
{
public LanguageMap Lang { get; set; }
public Dictionary<string, object> PageProperties { get; set; }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (PageProperties.ContainsKey(binder.Name))
{
result = PageProperties[binder.Name];
return true;
}
return false;
}
}
The problem with this is that properties declared inside a class which derives from DynamicObject will NOT be visible inside the page templates, it seems that only properties returned from TryGetMember are. And if I make the members visible the explicity checking for their names inside TryGetMember, then the indexing stops working just like in the case above.
Now if this was C++, I could create a preprocessor macro:
#define COMMON_MODEL_PROPERTIES \
Lang = _lang
More = _otherProperty
// Where models are declared:
var model = new
{
COMMON_MODEL_PROPERTIES,
PropX = "foo",
PropY = "bar"
}
but this isn't C++... It's C#.
Any ideas?
Ok, so as per my initial comment... just wow. I would highly recommend that you bite the bullet and refactor to use static typing. To anyone else reading this, please do not do this.
However if you really really really really really really really really want to achieve this with minimal changes, I think this will work for you.
Add an extension method to your codebase:
internal static class DynamicCommonPropertyExtensions
{
public static object AddCommonProperties(this object obj)
{
dynamic expando = new ExpandoObject();
foreach (var prop in obj.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public))
{
if (expando is IDictionary<string, object?> dict)
{
dict[prop.Name] = prop.GetValue(obj, null);
}
}
//Add common properties here
expando.CommonProp1 = "CommonPropValue1";
expando.CommonProp2 = "CommonPropValue2";
return expando;
}
}
This can then be used as follows (with your example)
var model = new
{
Lang = _lang,
PropX = "foo",
PropY = "bar"
};
var addedCommonProperties = model.AddCommonProperties();
RazorEngine.Run("templateA", addedCommonProperties);
Or even "better" without using another variable
var model = new
{
Lang = "Test",
PropX = "foo",
PropY = "bar"
}.AddCommonProperties();
RazorEngine.Run("templateA", model);
If you need to access the properties before passing through you can declare it as dynamic
dynamic model = new
{
Lang = "Test",
PropX = "foo",
PropY = "bar"
}.AddCommonProperties();
var a = model.CommonProp1;
I now feel really dirty having written this

Serialization/Deserialization without attributes in C#

I have a host of classes including the normal designs of object hierarchies and interfaces, base classes, etc. from a project where I cannot modify any code. I have another payload class in my project which uses composition to encapsulate information from the other classes and contain properties in the payload class whose types are the classes from the other project.
Now I have a need to be able to create an instance of the payload class containing instances of those other classes and serialize it to Base64 string for transmission.
Issue is since I cannot touch the code for the other classes, I cannot add serialization attributes (for .NET binary formatter/protobuf-net). I was also trying to look at using protobuf-net without attributes, but since the object hierarchy is too deep, it seemed to be too complicated to create.
Can somebody tell me a better choice to go ahead with the serialization/deserialization part without modifying the existing code.
Sample code to illustrate the requirement is shown below:
void Main()
{
var b = new B();
b.SetStatus("This is B");
var c = new C { Name = "C", Value = 100};
var payload = new Payload(b, c);
var serializedData = SerializeToString<Payload>(payload);
serializedData.Dump();
}
private static TData DeserializeFromString<TData>(string settings)
{
byte[] b = Convert.FromBase64String(settings);
using (var stream = new MemoryStream(b))
{
var formatter = new BinaryFormatter();
stream.Seek(0, SeekOrigin.Begin);
return (TData)formatter.Deserialize(stream);
}
}
private static string SerializeToString<TData>(TData settings)
{
using (var stream = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(stream, settings);
stream.Flush();
stream.Position = 0;
return Convert.ToBase64String(stream.ToArray());
}
}
public class A
{
public string Id { get; set; }
public string StatusMsg {get;protected set;}
public virtual void SetStatus(string msg)
{
StatusMsg = msg;
}
}
public class B : A
{
public B()
{
Id = new Guid().ToString();
}
public override void SetStatus(string msg)
{
base.SetStatus(msg);
}
}
public class C
{
public string Name
{
get;
set;
}
public Int32 Value
{
get;
set;
}
}
public class Payload
{
public B PropertyB { get; set; }
public C PropertyC { get; set; }
public Payload(B b, C c)
{
this.PropertyB = b;
this.PropertyC = c;
}
}
Without adding configuration attributes, you have a few options:
use a serialize that won't care: XmlSerializer or Json.NET might work if you're lucky
accept the runtime config work of something like protobuf-net or one of the others; it probably isn't as much work as you expect (heck, drop me an email with real code and I might be able to do it)
wrote the serialization entirely manually
write a DTO layer - a basic model with attributes etc that works well with your chosen serializer - and either write code to map between the two models, or use a tool like auto-mapper
Personally, when serialization configuration gets tricky, my default option is "write a DTO model".
I don't understand why protobuf would not work but you can do the serialization manually if you don't mind.
In that case, create a BinaryWriter and then write everything you need to be able to deserialize it back again using a BinaryReader. Doing it this way you will get a very compact representation tailored for you specific needs which is also very fast. But it's a bit more work also. General purpose serialization can be verbose and slow but is almost always preferred in any case.
But I still don't get why using any existing attribute-less serialization method wouldn't work. You should probably start by really understand why it doesn't work with for instance protobuf or JSON.NET. You mentioned "too deep hierarchy". How deep is that?

Serialize collection of base class to XML dynamically

O hai there,
I want to serialize and object, which looks like this:
public class Wrapper
{
[XmlArray("Entities"), XmlArrayItem("Entity")]
public List<Base> Entities { get; set; }
}
I want to keep application as flexible as possible, thus setting manually derived class types in XmlArrayItem attribute is not an option, how can I do it dynamically, i.e. make serializer aware of all derived classes.
Btw I have already a class to get all directly derived types like BaseDerived.DerivedClasses and XmlSerializer cs = new XmlSerializer(this.GetType(),BaseDerived.DerivedClasses); doesnt work...
Any idea?
Found a soultion, during serialization u can pretty much insert item attributes directly
So first you collect attributes like
XmlAttributeOverrides xOver = new XmlAttributeOverrides();
XmlAttributes xAttrs = new XmlAttributes();
foreach (var cls in BaseDerived.DerivedClasses)
{
var attr = new XmlArrayItemAttribute(cls);
xAttrs.XmlArrayItems.Add(attr);
}
xOver.Add(this.GetType(), BaseDerived.GetMemberName((Wrapper x) => x.Entities), xAttrs);
Btw:
public static string GetMemberName<T, TValue>(Expression<Func<T, TValue>> memberAccess)
{
return ((MemberExpression)memberAccess.Body).Member.Name;
}
and then you just mention it in serialization:
XmlSerializer cs = new XmlSerializer(this.GetType(), xOver);

Protobuf-net object reference deserialization using Dictionary: A reference-tracked object changed reference during deserialization

I'm having some issues trying to serialize/deserialize a complex object graph using protobuf-net.
I'm working on a legacy application and we're using .Net Remoting to connect a GUI client to a C# service. We are seeing poor performance with overseas users due to the serialized size of our object graphs using the default BinaryFormatter, which is exacerbated by the limited bandwidth in-between the client and server (1Mbit/s).
As a quick win, I thought I'd put together a proof of concept to see if there were any performance gains to be had by using protobuf-net instead, by implementing ISerializable. As I was testing I ran into an issue whereby object references weren't being maintained.
I've put together an example which repros the issue. I'm expecting that the object in the Dictionary (Items[1]) and the object B.A will be the same as I've specified AsReference=true in the ProtoMember attribute.
Using protobuf-net 2.0.0.619, I'm seeing an exception thrown when deserializing (A reference-tracked object changed reference during deserialization).
If this isn't a supported scenario the please let me know.
Test
[Test]
public void AreObjectReferencesSameAfterDeserialization()
{
A a = new A();
B b = new B();
b.A = a;
b.Items.Add(1, a);
Assert.AreSame(a, b.A);
Assert.AreSame(b.A, b.Items[1]);
B deserializedB;
using (var stream = new MemoryStream())
{
Serializer.Serialize(stream, b);
stream.Seek(0, SeekOrigin.Begin);
deserializedB = Serializer.Deserialize<B>(stream);
}
Assert.AreSame(deserializedB.A, deserializedB.Items[1]);
}
Class definitions
[Serializable]
[ProtoContract]
public class A
{
}
[Serializable]
[ProtoContract]
public class B
{
[ProtoMember(1, AsReference = true)]
public A A { get; set; }
[ProtoMember(2, AsReference = true)]
public Dictionary<int, A> Items { get; set; }
public B()
{
Items = new Dictionary<int, A>();
}
}
Edit: this should work from the next build onwards simply by marking the type's AsReferenceDefault:
[ProtoContract(AsReferenceDefault=true)]
public class A
{
// ...
}
At the current time this is sort of an unsupported scenario - at least, via the attributes it is unsupported; basically, the AsReference=true currently is referring to the KeyValuePair<int,A>, which doesn't really make sense since KeyValuePair<int,A> is a value-type (so this can never be treated as a reference; I've added a better message for that in my local copy).
Because KeyValuePair<int,A> acts (by default) as a tuple, there is currently nowhere to support the AsReference information, but that is a scenario I would like to support better, and I will be investigating this.
There was also a bug that meant that AsReference on tuples (even reference-type tuples) was getting out-of-order, but I've fixed that locally; this was where the "changed" message came from.
In theory, the work for me to do this isn't huge; the fundamentals already work, and oddly enough it came up separately on twitter last night too - I guess "dictionary pointing to an object" is a very common scenario. At a guess, I imagince I'll add some atribute to help describe this situation, but you can actually hack around it at the moment using a couple of different routes:
1: configure KeyValuePair<int,A> manually:
[Test]
public void ExecuteHackedViaFields()
{
// I'm using separate models **only** to keep them clean between tests;
// normally you would use RuntimeTypeModel.Default
var model = TypeModel.Create();
// configure using the fields of KeyValuePair<int,A>
var type = model.Add(typeof(KeyValuePair<int, A>), false);
type.Add(1, "key");
type.AddField(2, "value").AsReference = true;
// or just remove AsReference on Items
model[typeof(B)][2].AsReference = false;
Execute(model);
}
I don't like this much, because it exploits implementation details of KeyValuePair<,> (the private fields), and may not work between .NET versions. I would prefer to replace KeyValuePair<,> on the fly via a surrogate:
[Test]
public void ExecuteHackedViaSurrogate()
{
// I'm using separate models **only** to keep them clean between tests;
// normally you would use RuntimeTypeModel.Default
var model = TypeModel.Create();
// or just remove AsReference on Items
model[typeof(B)][2].AsReference = false;
// this is the evil bit: configure a surrogate for KeyValuePair<int,A>
model[typeof(KeyValuePair<int, A>)].SetSurrogate(typeof(RefPair<int, A>));
Execute(model);
}
[ProtoContract]
public struct RefPair<TKey,TValue> {
[ProtoMember(1)]
public TKey Key {get; private set;}
[ProtoMember(2, AsReference = true)]
public TValue Value {get; private set;}
public RefPair(TKey key, TValue value) : this() {
Key = key;
Value = value;
}
public static implicit operator KeyValuePair<TKey,TValue>
(RefPair<TKey,TValue> val)
{
return new KeyValuePair<TKey,TValue>(val.Key, val.Value);
}
public static implicit operator RefPair<TKey,TValue>
(KeyValuePair<TKey,TValue> val)
{
return new RefPair<TKey,TValue>(val.Key, val.Value);
}
}
This configures something to use instead of KeyValuePair<int,A> (converted via the operators).
In both of these, Execute is just:
private void Execute(TypeModel model)
{
A a = new A();
B b = new B();
b.A = a;
b.Items.Add(1, a);
Assert.AreSame(a, b.A);
Assert.AreSame(b.A, b.Items[1]);
B deserializedB = (B)model.DeepClone(b);
Assert.AreSame(deserializedB.A, deserializedB.Items[1]);
}
I do, however, want to add direct support. The good thing about both of the above is that when I get time to do that, you just have to remove the custom configuration code.
For completeness, if your code is using Serializer.* methods, then rather than create / configure a new model, you should configure the default model:
RuntimeTypeModel.Default.Add(...); // etc
Serializer.* is basically a short-cut to RuntimeTypeModel.Default.*.
Finally: you should not create a new TypeModel per call; that would hurt prerformance. You should create and configure one model instance, and re-use it lots. Or just use the default model.
I've setup a small test and found out that the AsReferenceDefault attribute doesn't work quite as expected.
Test class:
[ProtoContract(AsReferenceDefault = true)]
public class TEST
{
[ProtoMember(1018)]
public List<TEST> _Items { get; set; }
[ProtoMember(1001, AsReference = true)]
public TEST Parent;
[ProtoMember(1003)]
public string NameItemType;
public void AddItem(TEST Item)
{
_Items.Add(Item);
Item.Parent = this;
}
public TEST()
{
}
}
Test code:
TEST ci = new TEST(); ci._Items = new List<TEST>(); ci.NameItemType = "ROOT_ITEM";
TEST ci_2 = new TEST(); ci_2._Items = new List<TEST>(); ci_2.NameItemType = "ITEM_02"; ci.AddItem(ci_2);
TEST ci_3 = new TEST(); ci_3._Items = new List<TEST>(); ci_3.NameItemType = "ITEM_03"; ci_2.AddItem(ci_3);
// --> Confirm references.
bool AreEqual = false;
if (ci == ci_2.Parent)
AreEqual = true;
if (ci_2 == ci_3.Parent)
AreEqual = true;
// --> Serialize.
byte[] buf;
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
ProtoBuf.Serializer.Serialize(ms, ci);
buf = ms.ToArray();
}
// --> Deserialize.
using (System.IO.MemoryStream ms = new System.IO.MemoryStream(buf))
{
ci = ProtoBuf.Serializer.Deserialize<TEST>(ms);
}
// --> Confirm references.
ci_2 = ci._Items[0];
ci_3 = ci_2._Items[0];
if (ci == ci_2.Parent)
AreEqual = true;
if (ci_2 == ci_3.Parent) // HERE IS WHERE IT FAILS!
// THEY SHOULD BE EQUAL AFTER DESERIALIZATION!
AreEqual = true;
Update for those who might come here with the similar problem: starting from version 2.3.0 there is no need to use any tricks Marc mentioned above. Everything works as the Topic Starter wanted:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void AreObjectReferencesSameAfterDeserialization()
{
A a = new A();
B b = new B();
b.A = a;
b.Items.Add( 1, a );
Assert.AreSame( a, b.A );
Assert.AreSame( b.A, b.Items[ 1 ] );
B deserializedB;
var model = TypeModel.Create();
using( var stream = new MemoryStream() )
{
model.Serialize( stream, b );
stream.Seek( 0, SeekOrigin.Begin );
deserializedB = (B) model.Deserialize( stream, null, typeof(B) );
}
Assert.AreSame( deserializedB.A, deserializedB.Items[ 1 ] );
}
}
[ProtoContract]
public class A
{
}
[ProtoContract]
public class B
{
[ProtoMember( 1, AsReference = true )]
public A A { get; set; }
[ProtoMember( 2, AsReference = true )]
public Dictionary<int, A> Items { get; set; }
public B()
{
Items = new Dictionary<int, A>();
}
}

How to create a collection of classes that can be iterated over?

I have a series of properties for an object which are themselves a class:
private ClassThing Thing1;
private ClassThing Thing2;
private ClassThing Thing3;
private class ClassThing
{
public string Name;
public int Foos;
}
In some areas I need to be able to access each property specifically, for example:
label1.Text = Thing1.Name;
However, it is also desirable to create a foreach loop to access each one, like this:
string CombinedString;
foreach(ClassThing Thing in SomeCollection)
{
CombinedString += Thing.Name;
}
The end result must be XML serializable. These examples are very basic, but I hope they more easily demonstrate my need.
I tried creating a dictionary of these properties instead, but a dictionary is not XML serializable. I'd like to simply make all of these properties members of a class that itself can be iterated over, but I'm not sure how.
Can anyone point me in the right direction?
I hope this clarifies some things for you, since i am not entirely sure i understand your question.
//many normal classes can be made xml serializable by adding [Serializable] at the top of the class
[Serializable]
private class ClassThing
{
public string Name { get; set; }
public int Foos { get; set; }
}
//here we create the objects so you can access them later individually
ClassThing thing1 = new ClassThing { Name = "name1", Foos = 1 };
ClassThing thing2 = new ClassThing { Name = "name2", Foos = 2 };
ClassThing thing3 = new ClassThing { Name = "name3", Foos = 3 };
//this is an example of putting them in a list so you can iterate through them later.
List<ClassThing> listOfThings = new List<ClassThing>();
listOfThings.Add(thing1);
listOfThings.Add(thing2);
listOfThings.Add(thing3);
//iteration example
string combined = string.Empty;
foreach (ClassThing thing in listOfThings)
{
combined += thing.Name;
}
//you could also have created them directly in the list, if you didnt need to have a reference for them individually, like this:
listOfThings.Add(new ClassThing { Name = "name4", Foos = 4 });
//and more advanced concepts like linq can also help you aggregate your list to make the combined string. the foreach makes the code more readable though. this gives the same result as the foreach above, ignore it if it confuses you :)
string combined = listOfThings.Aggregate(string.Empty, (current, thing) => current + thing.Name);
//Here is an example of how you could serialize the list of ClassThing objects into a file:
using (FileStream fileStream = new FileStream("classthings.xml", FileMode.Create))
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<ClassThing>));
xmlSerializer.Serialize(fileStream, listOfThings);
}
To be able to serialize the objects using this method, they cannot contain a constructor, which is why we use the new ClassThing{Name="",Foos=0} way of creating them.
You're looking for an implementation of the IEnumerable interface. See this link for a quick description of how to implement it.
class MyClass
{
private ClassThing Thing1;
private ClassThing Thing2;
private ClassThing Thing3;
internal IEnumerable<ClassThing> GetThings()
{
yield return Thing1;
yield return Thing2;
yield return Thing3;
}
void Test()
{
foreach(var thing in this.GetThings())
{
//use thing
}
}
}
public List<ClassThing> Things = new List<ClassThing>();
Then you can run your foreach over .Things

Categories