如何控制复杂对象的反序列化

时间:2013-12-12 16:51:18

标签: .net xml vb.net serialization

我称第三方Web服务返回大量XML数据(大约2mb),我想以比正常更受控制的方式将其反序列化为对象。这是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对象。

请注意,这种事情通常是从WSDL生成的,但是,我没有提供一个,我没有XSD,trying to generate一个失败。我也知道我可以实现ISerializable并使用xml阅读器或linq,但同样,这可能会变成很多代码,也许很难维护。

4 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分: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;
}

答案 2 :(得分:1)

如果您不需要强类型,请考虑@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; }
    }
  }
}

答案 3 :(得分:0)

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

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