无法在C#中反序列化XML-无法识别的元素“添加”

时间:2018-07-09 18:54:25

标签: c# xml

我有一个使用自定义部分进行配置的C#应用​​。我将XML的那一部分定义为字符串。字符串如下所示:

var xml = @"<departments>
  <department id=""1"" name=""Sporting Goods"">
    <products>
      <product name=""Basketball"" price=""9.99"">
        <add key=""Color"" value=""Orange"" />
        <add key=""Brand"" value=""[BrandName]"" />
      </product>
    </products>
  </department>
</departments>"; 

此XML与我所描述的类here定义的架构匹配。当我将上面的字符串传递给Departments.Deserialize方法时,我收到一个错误。该错误显示:“无法识别的元素“添加””。调试器跳到我的Departments类中的这一行。

public void ReadXml(XmlReader reader)
{
  this.DeserializeElement(reader, false);
}

我认为错误是指“产品”元素中的“添加”元素。但是,Product ConfigurationElement具有一个名为KeyValueConfigurationCollection Items的属性。因此,add似乎可以工作。

为什么会出现此错误,如何解决我的代码,以便可以反序列化上面显示的XML字符串?

3 个答案:

答案 0 :(得分:0)

更新:长话短说-错误的序列化器。

除特殊的反序列化方法外,大多数代码都有效。

我能够纠正原始解决方案(here是我发现灵感的地方)。

我已更正且有效的部分(不要忘了原始post的IXmlSerializable):

private static string sectionName = "departments";

public static Departments Deserialize(string xml)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(Departments), new XmlRootAttribute(sectionName));
        Departments departments = null;

        var xdoc = XDocument.Parse(xml);
        departments = (Departments)serializer.Deserialize(xdoc.CreateReader());

        //var serializer = new XmlSerializer(typeof(Departments));
        //using (var reader = new StringReader(xml))
        //{
        //    departments = (Departments)(serializer.Deserialize(reader));
        //}

        return departments;
    }

希望它将帮助您继续前进。至少可以在我的机器上使用。让我知道是否需要完整的代码清单。

[保留原始解决方案,简单阅读建议的有问题的xml块]

  • 测试客户端。

class Program
{
    static void Main(string[] args)
    {
        string str = @"<departments>
                          <department id = ""1"" name = ""Sporting Goods"">
                               <products>
                                 <product name=""Basketball"" price=""9.99"">
                                      <add key = ""Color"" value = ""Orange""/>
                                      <add key = ""Brand"" value = ""[BrandName]""/>
                                 </product>
                               </products>
                          </department>
                       </departments>";

        XmlDocument xmlDoc = LoadXmlsFromString(str);

        string productXpath = "descendant-or-self::product";
        var nodes = ExtractNodes(xmlDoc, productXpath);
        foreach (XmlNode childrenNode in nodes)
        {
            var node = childrenNode.SelectSingleNode("descendant-or-self::*")?.OuterXml;
            var obj = Product.Deserialize(node);
        }
    }

    private static XmlNodeList ExtractNodes(XmlDocument xmlDoc, string xpath)
    {
        var nodes = xmlDoc.SelectNodes(xpath);
        return nodes;
    }

    private static XmlDocument LoadXmlsFromString(string str)
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.LoadXml(str);
        return xmlDoc;
    }
}
  • 通过为添加元素(和要转换成的元素对象)列表添加[XmlElement(“ add”)]属性来实现阅读产品。

[Serializable, XmlRoot("add")]
public class KeyValue
{
    [XmlAttribute(AttributeName = "key")]
    public string Key { get; set; }
    [XmlAttribute(AttributeName = "value")]
    public string Value { get; set; }

    public static KeyValue Deserialize(string xml)
    {
        KeyValue keyValue = null;
        var serializer = new XmlSerializer(typeof(KeyValue));
        using (var reader = new StringReader(xml))
        {
            keyValue = (KeyValue)(serializer.Deserialize(reader));
        }
        return keyValue;
    }
}

[Serializable, XmlRoot("product")]
public class Product
{
    [XmlAttribute(AttributeName = "name")]
    public string Name { get; set; }
    [XmlAttribute(AttributeName = "price")]
    public decimal Price { get; set; }
    [XmlElement("add")]
    public List<KeyValue> KeyValueList { get; set; }

    public static Product Deserialize(string xml)
    {
        Product product = null;
        var serializer = new XmlSerializer(typeof(Product));
        using (var reader = new StringReader(xml))
        {
            product = (Product)(serializer.Deserialize(reader));
        }

        return product;
    }
}

答案 1 :(得分:0)

我认为您必须阅读的配置不需要XmlSerializerSystem.Configuration为您提供的所有工具已绰绰有余。在这里,我重写了您的配置类(基本结构):

public class DepartmentsConfiguration : ConfigurationSection
{
    [ConfigurationProperty("departments", IsRequired = false, IsDefaultCollection = true)]
    public DepartmentItemCollection Departments
    {
        get
        {
            var departments = this["departments"] as DepartmentItemCollection;
            return departments;
        }
        set
        {
            this["departments"] = value;
        }
    }
}

[ConfigurationCollection(typeof(DepartmentItemCollection), AddItemName = "department")]
public class DepartmentItemCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new Department();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((Department)element).Name;
    }
}

public class Department : ConfigurationElement
{
    [ConfigurationProperty("id", IsRequired = false, IsKey = true)]
    public int Id
    {
        get
        {
            return (int)(this["id"]);
        }
        set
        {
            this["id"] = value;
        }
    }

    [ConfigurationProperty("name", IsRequired = true, IsKey = true, DefaultValue = "")]
    public string Name
    {
        get
        {
            return (string)(this["name"]);
        }
        set
        {
            this["name"] = value;
        }
    }

    [ConfigurationProperty("products", IsRequired = false, IsKey = false, IsDefaultCollection = false)]
    public ProductCollection Products
    {
        get
        {
            return (ProductCollection)this["products"];
        }
        set
        {
            this["products"] = value;
        }
    }
}

[ConfigurationCollection(typeof(ProductCollection), AddItemName = "product")]
public class ProductCollection : ConfigurationElementCollection
{
    protected override ConfigurationElement CreateNewElement()
    {
        return new Product();
    }

    protected override object GetElementKey(ConfigurationElement element)
    {
        return ((Product)element).Name;
    }
}

public class Product : ConfigurationElement
{
    [ConfigurationProperty("name", IsRequired = true, IsKey = true, DefaultValue = "")]
    public string Name
    {
        get
        {
            return (string)(this["name"]);
        }
        set
        {
            this["name"] = value;
        }
    }

    [ConfigurationProperty("price", IsRequired = false)]
    public decimal Price
    {
        get
        {
            return (decimal)(this["price"]);
        }
        set
        {
            this["price"] = value;
        }
    }

    [ConfigurationProperty("", IsRequired = false, IsKey = false, IsDefaultCollection = true)]
    [ConfigurationCollection(typeof(KeyValueConfigurationCollection), AddItemName = "add")]
    public KeyValueConfigurationCollection Items
    {
        get
        {
            var items = base[""] as KeyValueConfigurationCollection;
            return items;
        }
        set
        {
            base[""] = value;
        }
    }
}

App.config 中的<configSections>

  <configSections>
    <section 
        name="departmentConfiguration" 
        type="Test.DepartmentsConfiguration, Test" 
        allowLocation="true" 
        allowDefinition="Everywhere"
    />
  </configSections>
  <departmentConfiguration>
    <departments>
      <department id="1" name="Sporting Goods">
        <products>
          <product name="Basketball" price="9.99">
            <add key="Color" value="Orange" />
            <add key="Brand" value="[BrandName]" />
          </product>
        </products>
      </department>
    </departments>
  </departmentConfiguration>

以及如何使用ConfigurationManager进行阅读:

DepartmentsConfiguration config = (DepartmentsConfiguration) ConfigurationManager
    .GetSection("departmentConfiguration");

foreach (Department department in config.Departments)
{
    Console.WriteLine($"{department.Id}, {department.Name}");

    foreach (Product product in department.Products)
    {
        Console.WriteLine($"{product.Name}, {product.Price}");

        foreach (string key in product.Items.AllKeys)
        {
            Console.WriteLine($"{key} -> {product.Items[key].Value}");
        }
    }
}

我知道这与您的问题不一致,请作为个人建议。

答案 2 :(得分:0)

如何使用System.Xml.Linq XDocument

个人优点:

  • 简单
  • 没有第三方
  • 易于阅读,适用于较小的文件
  • 允许使用Linq语法

个人缺点:

  • 倾向于更大文件中的意大利面条式代码
  • 不是最快的解决方案

示例

static void Main(string[] args)
{
var xml =
@"<departments>
<department id=""1"" name=""Sporting Goods"">
<products>
<product name=""Basketball"" price=""9.99"">
<add key=""Color"" value=""Orange"" />
<add key=""Brand"" value=""[BrandName]"" />
</product>
</products>
</department>
</departments>";

var xDoc = XDocument.Load(new StringReader(xml));

var adds = xDoc.Root.Elements("department")
                    .Elements("products")
                    .Elements("product")
                    .Elements("add")
                    .Select(s => new KeyValuePair<string, string>(s.Attribute("key").ToString(), s.Attribute("value").ToString()))
                    .ToList();

foreach (var department in xDoc.Root.Elements("department"))
{
    Console.WriteLine("department: {0}", department.Attribute("name"));
    foreach (var products in department.Elements("products"))
    {
        foreach (var product in products.Elements("product"))
        {
            Console.WriteLine("product: {0}", product.Attribute("name"));
            foreach (var config in product.Elements("add"))
            {
                Console.WriteLine("add: {0} -> {1}", config.Attribute("key"), config.Attribute("value"));
            }
        }
    }
}