【问题标题】:Mapping XML file to a C# class将 XML 文件映射到 C# 类
【发布时间】:2016-02-20 03:46:13
【问题描述】:

我知道将 xml 反序列化为 C# 类是很常见的事情,而且我以前做过。但是我收到了一个 XML 文件,其格式很难正确反序列化。当我从 XML 生成 C# 类时。我得到了我不想要的格式。基本上所有的属性都得到了列类型..

基本的 XML 格式如下所示。问题是我有 250 个属性,我真的不想手动映射每个属性。

<item table="Order">
    <column columnName="Id"><![CDATA[2]]></column>
    <column columnName="Price"><![CDATA[200]]></column>
    <column columnName="Date"><![CDATA[25-01-2036 13:29:24:310]]>

我实际上已经手动编写了一个具有正确属性的类。 Id、price、date等等……

我尝试添加ElementName 和AttributeName,但没有成功。我是否可以使用XmlSerializer 将其直接映射到 C# 类?

它完美地映射了自动生成的类,但它最终得到了一个列和 columnName 的列表..

有谁知道我是否可以在我的 c# 类上使用一些 xml 符号来解决这个问题,以使其也能正确映射?

-------------- 解决办法--------------

感谢@Dan Field 让我走上正轨!

正如他自己指出的那样,他的解决方案中存在可空属性的问题。所以我又追捕了一个,这就是我想出的!

   public void ReadXml(XmlReader reader)
    {
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "column")
            {
                PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(EcomOrder));
                string propName = reader.GetAttribute("columnName");

                // Retrieve the property Descriptor
                PropertyDescriptor prop = props[propName];

                if (prop != null)
                {
                    reader.Read(); // move to CDATA (or text) node

                    // use DateTime.ParseExact instead for DateTime field
                    if (prop.PropertyType == typeof(DateTime?) || prop.PropertyType == typeof(DateTime))
                    {
                        prop.SetValue(this,
                             DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture));
                    }
                    else
                    {
                        prop.SetValue(this,
                             prop.Converter.ConvertFromInvariantString(reader.Value));
                    }
                }
                else
                {
                    throw new XmlException("Property not found: " + propName);
                }
            }

        }
    }

【问题讨论】:

  • 告诉我们你已经尝试过什么?
  • 向我们展示您用于反序列化的类。

标签: c# xml xml-parsing xmlserializer


【解决方案1】:

您可能会考虑使用 XSLT 将 XML 转换为 XmlSerializer 可以读取的内容,但如果只是这样,我认为您需要在自定义类上实现 IXmlSerializable。这是我拼凑起来的东西,使用反射将columnName 解析为Order 类中的一个属性。

如果您需要序列化回这种格式,这应该是相当简单的。

[XmlRoot("item")]
public class Order  : IXmlSerializable
{

    public int Id { get; set; }
    public int Price { get; set; }
    public DateTime Date { get; set; }

    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "column")
            {
                string propName = reader.GetAttribute("columnName"); // get the property info...
                PropertyInfo prop = typeof(Order).GetProperty(propName, 
                      BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty);
                if (prop != null && prop.CanWrite)
                {
                    reader.Read(); // move to CDATA (or text) node
                    // can use Convert.ChangeType for most types
                    // use DateTime.ParseExact instead for DateTime field
                    if (prop.PropertyType != typeof(DateTime))
                    {
                        prop.SetValue(this, 
                             Convert.ChangeType(reader.Value, prop.PropertyType, CultureInfo.InvariantCulture));
                    }
                    else
                    {
                        prop.SetValue(this, 
                             DateTime.ParseExact(reader.Value,  "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.CurrentCulture));
                    }
                }
                else
                {
                    throw new XmlException("Property not found: " + propName);
                }
            }                    

        }
    }

    public void WriteXml(XmlWriter writer)
    {
        throw new NotImplementedException();
    }
}

如果其中有任何其他类型需要特殊的解析注意事项,您可能需要做更多的工作。如果您获得 int 节点的空值,这将引发异常 - 如果是这种情况,可能仅使用 int? 即可。它也不包括任何类型的自动类生成 - 如果有很多属性在起作用或它们经常更改,我会编写一个实用程序来执行此操作。

【讨论】:

  • 有时间我一定会试试这个!当我希望它能正常工作时,我会再写一次! ;) 如果我理解正确,我可以像往常一样调用我的反序列化器,它会使用这个 readXml 而不是默认的?
  • 它肯定需要更多的充实才能真正使用 - 某些类型仅使用 Convert.ChangeType 不会正确转换,如果您有可空字段,它不会处理空值.
  • 是的,它会调用ReadXml 方法来实际进行反序列化工作。
  • 非常感谢!我找到了另一种使处理可空对象更容易的方法:) 我用解决方案更新了我的问题!现在我有最后一个问题要问你:D 我如何轻松地使测试方法也测试所有属性都得到正确的值? <.>
  • 如果您有解决方案,您应该将其发布为答案。如果您想接受它作为答案,那很好,尽管如果这个答案有帮助并且让您接受,您也可以接受这个(或至少投票)。就验证而言,我会编写一个单元测试方法,该方法具有一个嵌入式 XML 示例,您知道该示例应该使用有效值填充每个属性,并遍历该类型的 .GetProperties() 以确保每个属性都已设置。
【解决方案2】:

我认为不可能直接映射。 您可以使用 XPathNavigator 进行映射,也可以按照Marc Gravell in the following post 的建议进行操作 Deserialize xml base on attribute name as properties of my class in C#

【讨论】:

    【解决方案3】:

    感谢@Dan Field 让我走上正轨!

    正如他自己指出的那样,他的解决方案中存在可空属性的问题。所以我又追捕了一个,这就是我想出的!

       public void ReadXml(XmlReader reader)
        {
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element && reader.Name == "column")
                {
                    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(EcomOrder));
                    string propName = reader.GetAttribute("columnName");
    
                    // Retrieve the property Descriptor
                    PropertyDescriptor prop = props[propName];
    
                    if (prop != null)
                    {
                        reader.Read(); // move to CDATA (or text) node
    
                        // use DateTime.ParseExact instead for DateTime field
                        if (prop.PropertyType == typeof(DateTime?) || prop.PropertyType == typeof(DateTime))
                        {
                            prop.SetValue(this,
                                 DateTime.ParseExact(reader.Value, "dd-MM-yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture));
                        }
                        else
                        {
                            prop.SetValue(this,
                                 prop.Converter.ConvertFromInvariantString(reader.Value));
                        }
                    }
                    else
                    {
                        throw new XmlException("Property not found: " + propName);
                    }
                }
    
            }
        }
    

    【讨论】:

      猜你喜欢
      • 2014-05-04
      • 1970-01-01
      • 1970-01-01
      • 2011-09-28
      • 2021-10-25
      • 1970-01-01
      • 2012-03-09
      • 2012-02-13
      • 1970-01-01
      相关资源
      最近更新 更多