【问题标题】:Multiple Names for XML Elements and AttributesXML 元素和属性的多个名称
【发布时间】:2018-06-11 07:41:07
【问题描述】:

NOT A DUPLICATE OF THIS: 这已被标记为与我添加到现有 StackOverflow 问题和解决方案的链接的副本。这不是重复的,因为该问题专门针对 XmlElements。我正在寻找一种方法来塑造 solution 以使用 XmlAttributes 以及 XmlElements。


我正在构建一个与返回 XML 的 Web 服务交互的类库。此 Web 服务附加到文档管理系统。我为系统中的每种类型的实体(文件夹、文档、用户等)构建了一个对象。我目前有一个问题,其中有三个不同的操作返回指定目录中的文档列表。每个操作都以不同的格式返回文档元数据。

<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" />

所有这些元素都用于完全相同的文档。我想将它们中的每一个反序列化为同一个名为 Document 的对象,而不是为每个项目返回不同的类型。

我找到了一个很好的解决方案 here 来处理元素的同义词。唯一的问题是它没有显示我如何在属性中添加同义词。

我已尝试创建另一个为UnknownAttribute 事件调用的方法,但我没有成功。该方法只是上一个链接中的 SynonymHandler 方法的一个副本,但我稍作改动。

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);
        }
    }

我可能做错了,但我真的希望我能够避免在每种情况下手动处理属性。任何帮助表示赞赏。

编辑:当涉及到其他类型(例如文件夹)时,我也有同样的问题,所以我正在寻找一个单一的解决方案,而不是必须为发生这种情况的每个对象创建一个单独的反序列化器到。这就是我非常喜欢SynonymAttribute : Attribute 解决方案的原因。它将所有重要信息放在一起,并使用反射来查找我正在寻找的属性。

【问题讨论】:

  • @GaurangDave 这是我在帖子中的链接。它解释了如何使这项工作适用于 XmlElements,并特别指出该解决方案不适用于 XmlAttributes。我试图修改它以使其适用于 XmlAttributes 但我没有成功。

标签: c# xml xmlserializer


【解决方案1】:

使用 xml linq 您可以使用以下代码:

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; }

    }
}

【讨论】:

  • 我试图避免手动完成所有操作。因为我知道哪些 Web 服务调用提供了哪些信息,所以我可以根据在我确实需要这样做的场景中进行的调用来获取确切的属性,但是,我真的希望我可以继续使用 XmlSerializer .
  • 您可以将文件名替换为 URL。您可以将我的代码用作自定义序列化类的一部分,就像您正在做的那样。
  • 我使用的是 WSDL,所以我不确定 URL。 Web 服务的每个操作都返回 XmlNode,所以我绝对可以通过您的代码来实现,但我想将 XmlAttribute 的同义词与 Document Object 上的属性保持一致。如果我确实使用了这是一个解决方案,则意味着每次出现此类问题时我都需要创建一个新的反序列化器。我知道我对 Folder (folder, fol, f) 对象和 DocumentVersion (documentversion, dver, dv) 对象也有同样的问题。
  • 是否要保留属性值的重复项?类属性和名称可以是任何东西。在这种情况下,我经常使用 Dictionary 使结果更通用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多