.NET Forum / .NET Framework / New Users / August 2007
Xml serialization, arrays, XmlAnyAttribute
|
|
Thread rating:  |
Mark - 15 Aug 2007 20:12 GMT Hi...
I was trying to deserialize objects from xml that contain a couple of arrays. In the xml, at least, I was trying to express some default/static members to the array as attributes on the array bracket (e.g. ... <Foobars defaultFoo="bar"> <Foobar>Foo</Foobar> <Foobar></Foobar> </Foobars> ...
and in the class to be deserialized I have public Foobar[] Foobars {} )
I was experimenting with [XmlAnyAttribute], [XmlAttribute], etc and I found that the attribute defaultFoo just vaporizes. It doesn't show up in the any-attribute array for the container class nor does it show up in the unknown attributes array for the Foobar instances.
Ideally, what I'd like to have happen is to direct defaultFoo directed into public class Foobar { public static string defaultFoo; } but at the moment I'm not having luck getting it to show up anywhere.
Any pointers?
Thanks Mark
Steven Cheng[MSFT] - 16 Aug 2007 04:55 GMT Hi Mark,
From your description, you're using the .NET XmlSerializer to serialize/deserize some object Arrays and the serialized XML element has some attributes, you'e re wondering how to deserize those attributes into NET class's static members, correct?
Based on my understanding, the .NET Xmlserializer based xml serialization only working for mapping XML document/elements to .NET class instances(instance members), static members are not supported by the serialization engine.
And for your scenario that need to pickup some attributes from XML document and store into class's static members, I think you can consider either of the following means:
1. There are some community members who suggest use class instance members to wrapper static members. Thus, when setting the instance members, the static members are also updated. This is somewhat a trick, here is a thread mentioned this:
http://www.thescripts.com/forum/thread172026.html
2. Or if you're using .net framework 2.0, you can utilize the IXmlSerialization interface to completely control the serialization of a class(which implement this interface). You can completely write the code logic on how to class generate and write out its serialized XML output and how to read deserialized input xml from xmlreader and populate the class fields. here are some reference introducing this interface:
http://www.devx.com/dotnet/Article/29720
#IXmlSerializable Interface http://msdn2.microsoft.com/en-us/library/System.Xml.Serialization.IXmlSerial izable.aspx
Hope this helps. If you have any further questions, welcome to discuss here.
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
==================================================
Get notification to my posts through email? Please refer to http://msdn.microsoft.com/subscriptions/managednewsgroups/default.aspx#notif ications.
Note: The MSDN Managed Newsgroup support offering is for non-urgent issues where an initial response from the community or a Microsoft Support Engineer within 1 business day is acceptable. Please note that each follow up response may take approximately 2 business days as the support professional working with you may need further investigation to reach the most efficient resolution. The offering is not appropriate for situations that require urgent, real-time or phone-based interactions or complex project analysis and dump analysis issues. Issues of this nature are best handled working with a dedicated Microsoft Support Engineer by contacting Microsoft Customer Support Services (CSS) at http://msdn.microsoft.com/subscriptions/support/default.aspx.
==================================================
This posting is provided "AS IS" with no warranties, and confers no rights.
Mark - 17 Aug 2007 02:05 GMT Hi Steven...
Actually, that doesn't summarize my main questions. I'm sorry I wasn't more clear. My main questions were around
1) how XmlSerializer handles arrays of objects - the convention is to have an array type property/member (e.g. public Foobar[] Foobars {}). That serializes/deserializes as <Foobars><Foobar/><Foobar/></Foobars>.
and 2) how [XmlAnyAttribute] works.
The use case I was trying to implement involved adding an xml attribute to the array wrapper (e.g. <Foobars default="foo">...</Foobars>
If I have any single member of a serialized object, I can change the xml serialization with [XmlAttribute] (to make the member an attribute on the resulting xml) or [XmlElement].
The problem I have is that if I mark a member/property with [XmlAttribute] it becomes an attribute on the parent xml node not on another child member/property. For example the Foobars mentioned previously is a child member array of another object, so the whole xml would be something like <ObjectParent> ... <Foobars> <Foobar/> </Foobars> </ObjectParent>
I'd like to get the xml attribute associated with the array in the xml; I can work out how to push the value around later.
I tried using [XmlAnyAttribute] to capture that extra attribute on <Foobars default="..."> since it really didn't fit anywhere else. The problem there was that putting an attribute where I want doesn't show up in any [XmlAnyAttribute] bucket; it just disappears.
I tried putting an [XmlAnyAttribute] bucket in ObjectParent and in the definition for Foobar but neither one saw the default="" attribute.
I'll go look at the article about writing a whole serializer.
Thanks Mark
> Hi Mark, > [quoted text clipped - 66 lines] > > This posting is provided "AS IS" with no warranties, and confers no rights. Steven Cheng[MSFT] - 21 Aug 2007 05:41 GMT Hi Mark,
Any progress on this?
For the two questions you mentioned in last reply, I've performed some further research and here are some of what I found and understanding:
**1) how XmlSerializer handles arrays of objects - the convention is to have an array type property/member (e.g. public Foobar[] Foobars {}). That serializes/deserializes as <Foobars><Foobar/><Foobar/></Foobars>. ===================================== To take an Array member, you should use the "XmlArrayAttribute" to decorate the member property, and "XmlArrayItemAttribute" can help further describe the array item objects. Here is a test class I've made which can serialize and deserialize a class which take a member property of a custom collection class(array of another custom class):
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< namespace XMLSerializeApp { [XmlRoot("EmployeeElement")] public class Employee { private string _firstname; private string _lastname;
[XmlAttribute("firstName")] public string FirstName { get { return _firstname; } set { _firstname = value; } }
[XmlAttribute("lastName")] public string LastName { get { return _lastname; } set { _lastname = value; } }
public Employee() { }
public Employee(string fn, string ln) { _firstname = fn; _lastname = ln; } }
[XmlRoot("EmployeeCollectionElement")] public class EmployeeCollection : CollectionBase {
public Employee this[int index] { get { return ((Employee)List[index]); } set { List[index] = value; } }
public int Add(Employee value) { return (List.Add(value)); }
public int IndexOf(Employee value) { return (List.IndexOf(value)); }
public void Insert(int index, Employee value) { List.Insert(index, value); }
public void Remove(Employee value) { List.Remove(value); }
public bool Contains(Employee value) { // If value is not of type Employee, this will return false. return (List.Contains(value)); }
protected override void OnInsert(int index, Object value) { // Insert additional code to be run only when inserting values. }
protected override void OnRemove(int index, Object value) { // Insert additional code to be run only when removing values. }
protected override void OnSet(int index, Object oldValue, Object newValue) { // Insert additional code to be run only when setting values. }
protected override void OnValidate(Object value) { if (value.GetType() != typeof(Employee)) throw new ArgumentException("value must be of type Employee.", "value"); }
}
[XmlRoot("WorkGroupElement")] public class WorkGroup { private string _name; private EmployeeCollection _emps;
[XmlAttribute("name")] public string Name { get { return _name; } set { _name = value; } }
[XmlAnyAttribute] public XmlAttribute[] XAttributes;
[XmlArray("GroupMembers")] public EmployeeCollection Employees { get { return _emps; } set { _emps = value; } }
public WorkGroup() { _emps = new EmployeeCollection();}
public WorkGroup(string name) : this() { _name = name; } }
}
**2) how [XmlAnyAttribute] works. It seems the "XmlAnyAttribute attribute" only apply and work on the top level xml element( that means the type you supply in the XmlSerializer's constructor). for example, as for the classes I put above, If I deserlize the "WorkGroup" class, only the "XmlAnyAttribute" applied on WorkGroup class's member property will work(collect the attributes in WorkGroup element). If the "XmlAnyAttribute" is applied in nested property's class(such as the EmployeeCollection, it won't work ). This seems to be a fixed behavior of the xmlSerializer.
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
This posting is provided "AS IS" with no warranties, and confers no rights.
Mark - 21 Aug 2007 14:50 GMT Hi Steven...
Thanks for taking the time to work up an example. If I'm reading it correctly, you got near what I was looking to do (adding an xml attribute to an array) by adding another node to hang it on. In other words, I was looking to have something like
<GroupMembers name="something"> <Employee>...</Employee> </GroupMembers>
while your example would produce something like <WorkGroupElement name="something"> <GroupMembers> <Employee>...</Employee> </GroupMembers> </WorkGroupElement>
In short it doesn't seem like the form I wanted is possible with the given tools unless I write a completely custom serializer/deserializer. The least expensive work-around would be to add an additional parent element, as you did.
Thanks for all of your time on the questions.
Mark
Steven Cheng[MSFT] - 22 Aug 2007 07:13 GMT Thanks for your reply Mark,
Yes, due to the "XmlAnyAttribute attribute" behavior, if we want to make Custom collection/array an member of another class and also apply dynamic attributes on them, we may need to add a wrapper class(as XmlSerializer only look for attributes for the root class).
Also, if you want to manually do the serialization/deserialization work, I think you can consider create a custom collection class and implement the "IXmlSerializable" interface(I mentioned earlier), thus, you can also deal with the underlying xmlreader/xmlwriter to control the serialization/deserialzation without write a custom Xmlserializer. How do you think?
Anyway, please feel free to post here if there is anything else we can help.
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
This posting is provided "AS IS" with no warranties, and confers no rights.
Steven Cheng[MSFT] - 27 Aug 2007 03:13 GMT Hi Mark,
Any further questions on this? If so, please feel free to let me know.
Sincerely,
Steven Cheng
Microsoft MSDN Online Support Lead
This posting is provided "AS IS" with no warranties, and confers no rights.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|