1

NOT A DUPLICATE OF THIS: This has been flagged as a duplicate of the link that I added to an existing StackOverflow Question and Solution. This is not a duplicate as that question specifically deals with XmlElements. I am looking for a way to mould that solution to work with XmlAttributes as well as XmlElements.


I am building a Class Library that interacts with Web Service that returns XML. This Web Service is attached to a Document Management System. I have built an object for each type of entity that is in the system (Folder, Document, User, etc.). I currently have an issue where there are three different operations that return a list of documents that are in a specified directory. Each operation returns the document metadata in a different format.

<d id="1104" name="Intro.pdf" cdate="2018-06-08 13:27:05" size="188481" />

<d id="1104" n="Intro.pdf" s="188481" />

<document DocumentID="1104" Name="Intro.pdf" CreationDate="2018-06-08 13:27:05" Size="188481" />

All of these elements are for the exact same document. I am wanting to deserialise each of these into the same object called Document rather than return a different type for each item.

I found a great solution here for working with synonyms for elements. The only problem is that it does not show how I could add synonyms in for Attributes.

I have attempted to create another method that is called for the UnknownAttribute event but I have not been successful. The method is just a copy of the SynonymHandler method in the previous link, but I have altered it slightly.

protected void AttributeSynonymHandler(object sender, XmlAttributeEventArgs e)
    {
        //this part works as it returns the correct property to me
        var member = SynonymsAttribute.GetMember(e.ObjectBeingDeserialized, e.Attr.Name);
        Type memberType;

        if (member != null && member is FieldInfo)
            memberType = ((FieldInfo)member).FieldType;
        else if (member != null && member is PropertyInfo)
            memberType = ((PropertyInfo)member).PropertyType;
        else
            return;

        if (member != null)
        {
            //this is where the logic falls down, mainly because I don't have the original element anymore.
            object value;
            XmlSynonymDeserializer serializer = new XmlSynonymDeserializer(memberType, new XmlRootAttribute(e.Attr.Name));
            using (System.IO.StringReader reader = new System.IO.StringReader(e.Attr.OuterXml))
                value = serializer.Deserialize(reader);

            if (member is FieldInfo)
                ((FieldInfo)member).SetValue(e.ObjectBeingDeserialized, value);
            else if (member is PropertyInfo)
                ((PropertyInfo)member).SetValue(e.ObjectBeingDeserialized, value);
        }
    }

I am probably going about it wrong, but I was really hoping that I would be able to avoid manually handling the attributes in each case. Any help is appreciated.

EDIT: I also have the same issue when it comes to other types (e.g. Folders) so I am looking for a single solution rather than having to create a separate deserialiser for each object that this happens to. That is why I really liked the SynonymAttribute : Attribute solution. It kept all the important information together and used Reflection to find which Attribute I was looking for.

spovelec
  • 369
  • 1
  • 4
  • 20
  • @GaurangDave That is the link that I had in my post. It explains how to make this work for XmlElements and specifically states that the solution would not work with XmlAttributes. I have attempted to modify it so that it works for XmlAttributes but I have not been successful. – spovelec Jun 11 '18 at 08:14

1 Answers1

0

Using xml linq you can use following code :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication48
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);

            var groups = (from d in doc.Descendants("d")
                             join xdoc in doc.Descendants("document") on (string)d.Attribute("id") equals (string)xdoc.Attribute("DocumentID")
                             select new List<XElement> { d, xdoc })
                             .GroupBy(x => (string)x.FirstOrDefault().Attribute("id"))
                             .Select(x => x.SelectMany(y => y).ToList())
                             .ToList();
            List<Document> documents = new List<Document>();
            foreach (var group in groups)
            {
                Document newDoc = new Document();
                documents.Add(newDoc);
                foreach (XElement element in group)
                {
                    foreach (XAttribute attribute in element.Attributes())
                    {
                        switch (attribute.Name.LocalName.ToUpper())
                        {
                            case "ID" :
                                newDoc.id = (string)attribute;
                                break;
                            case "DOCUMENTID":
                                newDoc.id = (string)attribute;
                                break;
                            case "NAME":
                                newDoc.name = (string)attribute;
                                break;
                            case "N":
                                newDoc.name = (string)attribute;
                                break;
                            case "CDATE":
                                newDoc.date = (DateTime)attribute;
                                break;
                            case "CREATIONDATE":
                                newDoc.date = (DateTime)attribute;
                                break;
                            case "SIZE":
                                newDoc.size = (long)attribute;
                                break;
                            case "S":
                                newDoc.size = (long)attribute;
                                break;
                            default :
                                break;

                        }
                    }
                }

            }
        }
    }
    public class Document
    {
        public string id { get; set; }
        public string name {get; set; }
        public long size { get; set; }
        public DateTime date { get; set; }

    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • 2
    I'm trying to avoid having to do it all manually. Since I know which web service calls provide which bits of information, I can grab the exact attributes depending on which call is made in the scenario that I do need to go that way, but, I am really hoping that I can keep using the XmlSerializer. – spovelec Jun 11 '18 at 11:16
  • You can replace the filename with a URL. You can use my code to as part of a custom serialize class like what you are doing. – jdweng Jun 11 '18 at 13:53
  • I am using a WSDL so I am not sure of the URLs. Every Operation of the Web Service returns XmlNode, so I could definitely put that through your code, but I would like to keep the synonyms of the XmlAttribute with the property on the Document Object. If I did use this is a solution it would mean that I would need to create a new Deserialiser every time an issue like this arises. I know that I also have the same issues with Folder (folder, fol, f) objects and DocumentVersion (documentversion, dver, dv) objects. – spovelec Jun 11 '18 at 22:55
  • Do you want to keep duplicates of the attribute values? The class properties and names can be anything. I often in cases like this use a Dictionary which makes results more generic. – jdweng Jun 12 '18 at 00:51