【问题标题】:How to control the de-serialisation of a complex object如何控制复杂对象的反序列化
【发布时间】:2013-12-12 16:51:18
【问题描述】:

我调用了返回大量 XML 数据(大约 2mb)的第三方 Web 服务,并且我想以比正常方式更可控的方式将其反序列化为对象。这是xml:

<vehicle>
  <vehicleManufacturer>
    <type>1</type>
    <name>A Make</name>
  </vehicleManufacturer>
...

我知道我可以执行以下代码来控制哪些元素被序列化为哪些属性,在这种情况下,Car Make 对象包含两个属性,类型和名称。

    <System.Xml.Serialization.XmlElement("vehicleManufacturer")> _
    Public Property Make() As CarMake

但如果可能的话,我想做的是将名称字段反序列化到 Make 属性中,或者甚至只是将整个元素及其内容序列化为文本。

    <System.Xml.Serialization.XmlElement("vehicleManufacturer")> _
    Public Property Make() As String

这是一个非常简化的示例,所以如果有任何资源可以帮助我解决这个问题,那就太好了。

另一个例子:

<vehicle>
  <warrantyDetail>
    <warrantyBasicInformation buildStartdate="09/07/2013" warrantyStartDate="31/07/2013"/>
  </warrantyDetail>
</vehicle>

我想从warrantyBasicInformation 中获取属性,而不必构建保修详细信息和warrantyBasicInformation 对象。

请注意,这种东西通常是从 WSDL 生成的,但是,我不会得到一个,我没有 XSD,而且trying to generate 一个失败了。我也知道我可以实现 ISerializable 并使用 xml 阅读器或 linq,但同样,这可能会变成大量代码并且可能难以维护。

【问题讨论】:

    标签: .net xml vb.net serialization


    【解决方案1】:

    几周前我遇到过类似的情况,您尝试使用的 Microsoft 的 xsd.exe 未能生成合理的 XML 架构。我使用trang 解决了这个问题,它完美地完成了这项工作。然后我使用 xsd.exe 自动生成包装类。

    这可能无法完全回答您的问题,但使用 xsd 架构应该会让您的生活更轻松。

    【讨论】:

    • 看起来很有用,并且 XSD 生成正确,但使用 XSD 生成类产生了大约 10 个错误,说:“架构验证警告:'w3.org/2001/XMLSchema-instance:type' 属性未声明。第 376 行,位置8."然后警告:“无法验证架构。类生成可能会失败或可能产生不正确的结果。没有生成类。”
    【解决方案2】:

    我们一直在使用Xsd2Code 来读取 xml 文件。所有这些文件背后都有一个有助于生成的 XSD(我认为 XSD 是您在没有大量手工代码的情况下解决此问题的唯一方法,如果出现问题,请尝试自己编写,这并不难) .

    Xsd2Code 使用 Serialize 和 Deserialize 方法生成代码文件,如下所示:

    repositoryAsXml = settings.Serialize();
    

    icn_database_v database;
    
    if (!icn_database_v.Deserialize(xmlContents, out database, out exception))
    {
        throw exception;
    }
    

    【讨论】:

    • 由于时间限制,我想尽可能地避免手工工作......对象很大,我不需要包含所有数据,我只是想挑选一些元素。我没有 XSD,所以我不确定 Xsd2Code 将如何提供帮助?
    • 我们也使用了您首先尝试的解决方案(生成 xsd,并使用 xsd.exe 生成 C# 类)。这对我们也不起作用。 Xsd2Code 具有从 XSD 生成代码的更好方法。因此,当您选择示例 xml,从中生成 XSD(使用 Visual Studio 或在线工具)并从中生成代码时,它不需要手动工作(我们使用自动构建脚本为我们完成这一切)。
    • 我尝试了许多工具来从 XML 获取 XSD,但它们要么有错误,要么产生不准确的 XSD。我认为它是不准确的,因为 Xsd2Code 在序列化和反序列化时出现错误,并显示消息“反映类型的错误”和“不一致的序列:如果在类的成员之一上使用,则所有类似粒子都需要 'Order' 属性成员,请使用类成员 'vin' 上的 XmlElement、XmlAnyElement 或 XmlArray 自定义属性显式设置 'Order'。"
    • 如果你有一个例子,我很乐意帮助你。
    • 不,只有我在下面回答的内容。它有效,但我没时间找到“合适”的解决方案(......通常似乎是商业软件的情况)
    【解决方案3】:

    如果您不需要强类型,请考虑@Esen 在XML class generator for serialization 提供的以下自行解决方案:

    namespace Utility1 
    {
    public static class XMLHelper
    {
       private enum XMLType
       {
          Element,
          Attribute
       }
        public static string GenerateXMLClass(string xmlstring)
        {
            XmlDocument xd = new XmlDocument();
            xd.LoadXml(xmlstring);
            XmlNode rootNode = xd.DocumentElement;
            var xmlClassCollection = new Dictionary<string, XMLClass>();
            var xmlClass = new XMLClass();
            xmlClassCollection.Add(rootNode.Name, xmlClass);
            CollectAttributes(ref xmlClass, rootNode);
            CollectElements(ref xmlClass, rootNode);
            CollectChildClass(ref xmlClassCollection, rootNode);
    
            var clsBuilder = new StringBuilder();
    
            clsBuilder.AppendLine("[XmlRoot(\"" + rootNode.Name + "\")]");
    
            foreach (var cls in xmlClassCollection)
            {
                clsBuilder.AppendLine("public class " + cls.Key);
                clsBuilder.AppendLine("{");
    
                foreach (var element in cls.Value.Elements)
                {
                    if (XMLType.Element == element.XmlType)
                        clsBuilder.AppendLine("[XmlElement(\"" + element.Name + "\")]");
                    else
                        clsBuilder.AppendLine("[XmlAttribute(\"" + element.Name + "\")]");
                    clsBuilder.AppendLine("public " + element.Type + element.Name + "{get;set;}");
                }
    
                clsBuilder.AppendLine("}");
            }
    
            return clsBuilder.ToString();
        }
    
        private static void CollectAttributes(ref XMLClass xmlClass, XmlNode node)
        {
            if (null != node.Attributes)
            {
                foreach (XmlAttribute attr in node.Attributes)
                {
                    if (null == xmlClass.Elements.SingleOrDefault(o => o.Name == attr.Name))
                        xmlClass.Elements.Add(new Element("string ", attr.Name, XMLType.Attribute));
                }
            }
        }
    
        private static bool IsEndElement(XmlNode node)
        {
            if ((null == node.Attributes || node.Attributes.Count <= 0) &&
                       (null == node.ChildNodes || !node.HasChildNodes || (node.ChildNodes.Count == 1 && node.ChildNodes[0].NodeType == XmlNodeType.Text)))
            {
                return true;
            }
            return false;
        }
    
        private static void CollectElements(ref XMLClass xmlClass, XmlNode node)
        {
            foreach (XmlNode childNode in node.ChildNodes)
            {
                if (null == xmlClass.Elements.SingleOrDefault(o => o.Name == childNode.Name))
                {
                    var occurance = node.ChildNodes.Cast<XmlNode>().Where(o => o.Name == childNode.Name).Count();
                    var appender = "  ";
                    if (occurance > 1)
                        appender = "[] ";
    
                   if(IsEndElement(childNode))
                    {
                        xmlClass.Elements.Add(new Element("string" + appender, childNode.Name, XMLType.Element));
                    }
                    else
                    {
                        xmlClass.Elements.Add(new Element(childNode.Name + appender, childNode.Name, XMLType.Element));
                    }
                }
            }
        }
    
        private static void CollectChildClass(ref Dictionary<string, XMLClass> xmlClsCollection, XmlNode node)
        {
            foreach (XmlNode childNode in node.ChildNodes)
            {
                if (!IsEndElement(childNode))
                {
                    XMLClass xmlClass;
                    if (xmlClsCollection.ContainsKey(childNode.Name))
                        xmlClass = xmlClsCollection[childNode.Name];
                    else
                    {
                        xmlClass = new XMLClass();
                        xmlClsCollection.Add(childNode.Name, xmlClass);
                    }
                    CollectAttributes(ref xmlClass, childNode);
                    CollectElements(ref xmlClass, childNode);
                    CollectChildClass(ref xmlClsCollection, childNode);
                }
            }
        }
    
        private class XMLClass
        {
            public XMLClass()
            {
                Elements = new List<Element>();
            }
            public List<Element> Elements { get; set; }
        }
    
        private class Element
        {
            public Element(string type, string name, XMLType xmltype)
            {
                Type = type;
                Name = name;
                XmlType = xmltype;
            }
            public XMLType XmlType { get; set; }
            public string Name { get; set; }
            public string Type { get; set; }
        }
      }
    }
    

    【讨论】:

    【解决方案4】:

    我觉得这是一种代码异味,但它是我想出的最简单的方法。 (它假设节点将存在等,在这种情况下是可以的。)

    Using tr As IO.TextReader = New IO.StringReader(strResult)
    
      Using reader As System.Xml.XmlReader = System.Xml.XmlReader.Create(tr)
    
        reader.MoveToContent()
    
        While reader.Read
    
          If reader.NodeType = Xml.XmlNodeType.Element Then
    
            Select Case reader.Name
    
              Case "warrantyDetail"
    
                Dim xel As XElement = CType(XNode.ReadFrom(reader), Xml.Linq.XElement)
    
                If xel IsNot Nothing Then
    
                  vi.RegCountry = xel.Element("country").Element("name").Value
                  vi.Reg = xel.Element("registration").Value
                  xel.Element("warrantyBasicInformation").Attribute("buildStartdate").Value
                  xel.Element("warrantyBasicInformation").Attribute("buildStartdate").Value
                  ...
    
                End If
    
              Case ...
    
            End Select
          End If    
        End While
      End Using
    End Using
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-20
      • 1970-01-01
      相关资源
      最近更新 更多