I found something that confused me today:
1. If I have this:
public interface INamed
{
[XmlAttribute]
string Name { get; set; }
}
public class Named : INamed
{
public string Name { get; set; }
}
It gives the following output (Name property serialized as element):
<Named>
<Name>Johan</Name>
</Named>
2. If I have this:
public abstract class NamedBase
{
[XmlAttribute]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
public override string Name { get; set; }
}
The XmlSerializer throws System.InvalidOperationException
Member 'NamedDerived.Name' hides inherited member 'NamedBase.Name',
but has different custom attributes.
The code I used for serialization:
[TestFixture]
public class XmlAttributeTest
{
[Test]
public void SerializeTest()
{
var named = new NamedDerived {Name = "Johan"};
var xmlSerializer = new XmlSerializer(named.GetType());
var stringBuilder = new StringBuilder();
using (var stringWriter = new StringWriter(stringBuilder))
{
xmlSerializer.Serialize(stringWriter, named);
}
Console.WriteLine(stringBuilder.ToString());
}
}
My question is:
Am I doing it wrong and if so what is the correct way to use xml attributes in interfaces and abstract classes?
Attributes are not inherited on overriden properties. You need to redeclare them.
Also in your first example the behavior is not the "expected" one as you declared XmlAttribute at the interface level and yet the serialized xml contains the value as an element. So the attribute in the interface is ignored and only info taken from the actual class matters.
I think you should xmlignore your abstract class property
public abstract class NamedBase
{
[XmlIgnore]
public abstract string Name { get; set; }
}
public class NamedDerived : NamedBase
{
[XmlAttribute]
public override string Name { get; set; }
}
Related
I am using protobuf-net version 2.3.2.0.
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
[ProtoInclude(12, typeof(SubClass))]
public class BaseClass
{
public string Id { get; set; }
public string Name { get; set; }
}
[ProtoContract(ImplicitFields = ImplicitFields.AllFields)]
public class SubClass : BaseClass
{
public string PropertySub { get; set; }
}
As my base class is inherited from many child classes.
How can I avoid placing attribute [ProtoInclude] on my base classes so that props in my base class deserialize as expected.
I created the following abstract class:
public abstract class AbstractClass
{
public abstract string Name { get; set; }
public abstract object Value { get; set; }
}
Now I want to derive two classes of the abstract class. I want to use an enum instead of the type object. My derived classes look like this:
First class:
public class InheritanceClass1:AbstractClass
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
}
Second class:
public class InheritanceClass2 : AbstractClass
{
public override string Name { get; set; }
public override SecondEnum Value { get; set; }
}
I'm getting an error showed in my code, that the type of the property Value isn't object. I tryed to use the new-keyword instead of override like this:
In my abstract class:
public object Value { get; set; }
In my derived class:
public new FirstEnum Value { get; set; }
But if I create a List<AbstractClass> I have the problem that I can't use it for example for Linq because I would retrieve the "wrong" property. It is just hided, but still there, so I have to override the property.
So how do I have to change my abstract class and my derived classes, that I can use different types in my derived classes?
You can use abstract class like this:
public abstract class AbstractClass<T>
{
public abstract string Name { get; set; }
public abstract T Value { get; set; }
}
And derived class will change like this:
public class InheritanceClass1 : AbstractClass<FirstEnum>
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
}
If you know that you will need only enums, you can add struct, IConvertible restriction to T:
public abstract class AbstractClass<T> where T : struct, IConvertible
{
public abstract string Name { get; set; }
public abstract T Value { get; set; }
}
Update based on comment:
Not the cleanest solution if you need List<AbstractClass>, but you can have additional class:
public abstract class AbstractClass
{
public abstract string Name { get; set; }
public abstract int GetValue ();
}
Which will then be inherited by AbstractClass<T>:
public abstract class AbstractClass<T> : AbstractClass where T : struct, IConvertible
{
public abstract T Value { get; set; }
}
And InheritancClass:
public class InheritanceClass1 : AbstractClass<FirstEnum>
{
public override string Name { get; set; }
public override FirstEnum Value { get; set; }
public override int GetValue () => (int)Value;
}
And then you can use it in a list:
var list = new List<AbstractClass> { new InheritanceClass1 (), new InheritanceClass2 () };
In this way you can use List<AbstractClass> with GetValue method. If you are using only enums you can always recast it to enum value. Ofcorse, you would not know exactly which enum it is, but you can add additional field for that.
I am using Newtonsoft JSON serializer, and the serialized string is missing the properties from the derived class if the class is derived from a list. Here is my example code.
Classes:
[DataContract]
public class TestItem
{
[DataMember]
public int itemInt;
[DataMember]
public string itemString;
public TestItem() {}
public TestItem(int _intVal, string _stringVal)
{
itemInt = _intVal;
itemString = _stringVal;
}
}
[DataContract]
public class TestMain : List<TestItem>
{
[DataMember]
public int mainInt;
[DataMember]
public string mainString;
}
Serializing code:
string test;
// Test classes
TestMain main = new TestMain();
main.mainInt = 123;
main.mainString = "Hello";
main.Add(new TestItem(1, "First"));
test = Newtonsoft.Json.JsonConvert.SerializeObject(main);
After serialization, the value of test is:
[{\"itemInt\":1,\"itemString\":\"First\"}]
The values for mainInt and mainString are missing altogether.
The behaviour is not changed by the [DataContract] and [DataMember] tags, but I have them in there, to pre-empt the answer that they are missing.
How do I get JSON to recognize and serialize the mainInt and mainString properties of the derived class?
Is this what you want?
[DataContract]
public class TestItem
{
[DataMember]
public int itemInt { get; set; }
[DataMember]
public string itemString { get; set; }
public TestItem() { }
public TestItem(int _intVal, string _stringVal)
{
itemInt = _intVal;
itemString = _stringVal;
}
}
[DataContract]
public class TestMain
{
[DataMember]
public int mainInt { get; set; }
[DataMember]
public string mainString { get; set; }
[DataMember]
public List<TestItem> TestItem = new List<TestItem>();
}
class Program
{
static void Main(string[] args)
{
string test;
// Test classes
TestMain main = new TestMain();
main.mainInt = 123;
main.mainString = "Hello";
main.TestItem.Add(new TestItem(1, "First"));
test = Newtonsoft.Json.JsonConvert.SerializeObject(main);
Console.WriteLine(test);
}
}
Take a look at putting json attribs on your properties. Here is a sample: Json.NET serialize object with root name. The only thing I would hesitate to do is having the main derive from list like that. Its not a recommended pattern and practice.
add list as another child property rather then deriving from list, json serialize is getting confused as to your intentions.
adding this attrib works for me:
using System.ComponentModel.DataAnnotations;
[Newtonsoft.Json.JsonObject(Title = "root")]
public class Testmain : List
Using .net XmlSerializer and the following structure:
public class SomeClass {
[XmlElement("some-string")]
public string SomeString { get; set; }
}
I need the above to produce :
<someclass>
<some-string alt-name="someotherstring">
StringValue
</some-string>
</someclass>
But i dont want to have to define types for somestring, some int, somebool, yetanotherstring etc every time i want to add a standard type as a porperty to my classes.
Any way I can override xlement to handle this maybe?
Produce wrappers for base types and conversion operators to alleviate object construction:
[Serializable()]
public partial class StringWrapper
{
[XmlAttribute("alt-name")]
public string altname { get; set; }
[XmlText()]
public string Value { get; set; }
public static implicit operator string (StringWrapper sw) { return sw.Value; }
public static implicit operator StringWrapper (string s) {
return new StringWrapper() { altname = "someotherstring", Value = s };
}
}
Use wrappers instead of base types where needed:
[Serializable()]
[XmlRoot(Namespace = "someclass", IsNullable = false)]
public class someclass
{
[XmlElement("some-string")]
public StringWrapper somestring { get; set; }
}
Use it like:
var srlz = new XmlSerializer(typeof(someclass));
srlz.Serialize(Console.Out, new someclass() { somestring = "StringValue" });
The only way to do that via XmlSerializer is:
[XmlRoot("someclass")]
public class SomeClass {
[XmlElement("some-string")]
public SomeOtherClass Foo {get;set;}
}
public class SomeOtherClass {
[XmlText]
public string Text {get;set;}
[XmlAttribute("alt-name")]
public string Bar {get;set;}
}
Alternatively: use XmlDocument / XDocument instead of XmlSerializer.
I have the following classes. I want do not want the derived class to serialize "Name", however, the following code does not seem to work. if I declare an instance of the derived class, the "Name" property still gets serialized..
[DataContract]
public class Base
{
[DataMember]
public virtual string Name
{get; set; }
}
[DataContract]
public class Derived: Base
{
[IgnoreDataMember]
public override string Name
{get; set; }
}
what if you declare the property virtual in the Base class? And then override it in the derived class
Try using the ScriptIgnore (json) or XmlIgnore (xml) attributes, depending on what you're serializing to. That should prevent that member from being serialized.
If you want to try to use XmlSerializer here is the code
public class Base
{
[XmlIgnore]
public virtual string Name { get; set; }
}
public class Derived : Base
{
[XmlIgnore] //Remove this to include
public override string Name { get; set; }
}
MemoryStream memStream = new MemoryStream();
XmlSerializer xmlSer = new XmlSerializer(typeof(Derived));
xmlSer.Serialize(memStream, new Derived() { Name = "aaaa" });
MessageBox.Show(Encoding.UTF8.GetString(memStream.ToArray()));