【问题标题】:Best way to parse XML Attributes解析 XML 属性的最佳方法
【发布时间】:2012-07-20 04:22:40
【问题描述】:

我有以下需要解析的 XML 文档:

...
<tx size_total="143">
  <type size="1" start_key="02">STX</type>
  <type size="3">Type</type>
  <type size="3" decimal="true">Serial</type>
  <type size="3" key="23 64 31">Function_Code</type>
  <type size="2" decimal="true">LIU</type>
  <type size="1">Status</type>
  <type size="2" repeat="64" binary ="true" binary_discard="2">Value</type>
  <type size="1">ETX</type>
  <type size="1">LRC</type>
...

我写了如下代码进行解析:

XmlNodeList typeNodeList = txNode.SelectNodes(TYPE_NODE);
CommRuleContainer rc = new CommRuleContainer(funcNode.Attributes.GetNamedItem("name").Value,
                        txNode.Attributes.GetNamedItem("size_total").Value, funcNode.Attributes.GetNamedItem("id").Value);
foreach (XmlNode tNode in typeNodeList)
{
    int size = Convert.ToInt32(tNode.Attributes.GetNamedItem("size").Value);
    int repeat = Convert.ToInt32(tNode.Attributes.GetNamedItem("repeat").Value);
    int binary_discard = Convert.ToInt32(tNode.Attributes.GetNamedItem("binary_discard").Value);
    string start_key = tNode.Attributes.GetNamedItem("start_key").Value;
    string key = tNode.Attributes.GetNamedItem("key").Value;
    bool convert_decimal = false, convert_binary = false;
    if (tNode.Attributes.GetNamedItem("decimal").Value == "true")
                                convert_decimal = true;
    if (tNode.Attributes.GetNamedItem("binary").Value == "true")
                                convert_binary = true;
    rc.AddTypeDefinition(tNode.Value, size, repeat, binary_discard, convert_decimal, convert_binary);
}

如果我尝试获取不存在的某些属性的值(即:tNode.Attribute.GetNamedItem("repeat").value 在所有没有重复的节点上失败),代码将引发 nullreferenceexception属性)。有什么方法可以验证某个属性是否存在?

此外,上面的代码一点也不干净。组织上述代码的最佳方式是什么?

编辑:我知道您可以在从属性中获取值之前单独检查属性是否为空的方法,但这会使代码看起来很脏,因为我需要编写很多 ifs(或嵌套如果)

if (tNode.Attributes.GetNamedItem("decimal") != null)
   if (tNode.Attributes.GetNamedItem("decimal").Value == "true")
       convert_decimal = true;

从长远来看,如果我必须编写更多属性,这将成为问题。我想了解更多有组织的方法(也许可以枚举 XML 属性?我不知道。)

【问题讨论】:

    标签: c# .net xml winforms


    【解决方案1】:

    同意@nunespascal,这是我已经为你准备的代码..他回答得比我快..哈哈:

    static void Main(string[] args)
            {
                var serialized = @"
    <tx size_total=""143""> 
      <type size=""1"" start_key=""02"">STX</type> 
      <type size=""3"">Type</type> 
      <type size=""3"" decimal=""true"">Serial</type> 
      <type size=""3"" key=""23 64 31"">Function_Code</type> 
      <type size=""2"" decimal=""true"">LIU</type> 
      <type size=""1"">Status</type> 
      <type size=""2"" repeat=""64"" binary =""true"" binary_discard=""2"">Value</type> 
      <type size=""1"">ETX</type> 
      <type size=""1"">LRC</type></tx>";
                var deserialized = serialized.XmlDeserialize<Tx>();
            }
        }
    
        [XmlRoot("tx")]
        public class Tx
        {
            [XmlAttribute("size_total")]
            public int TotalSize { get; set; }
    
            [XmlElement("type")]
            public List<TxType> Types { get; set; }
    
            public Tx()
            {
                Types = new List<TxType>();
            }
        }
    
        public class TxType
        {
            [XmlAttribute("size")]
            public string Size { get; set; }
    
            [XmlAttribute("decimal")]
            public bool IsDecimal { get; set; }
    
            [XmlAttribute("binary")]
            public bool IsBinary { get; set; }
    
            [XmlAttribute("start_key")]
            public string StartKey { get; set; }
    
            [XmlAttribute("key")]
            public string Key { get; set; }
    
            [XmlAttribute("repeat")]
            public int Repeat { get; set; }
    
            [XmlAttribute("binary_discard")]
            public int BinaryDiscard { get; set; }
    
            [XmlText]
            public string Value { get; set; }
        }
    

    这是我的反序列化助手类:

    public static class StringExtensions
        {
            /// <summary>
            /// Deserializes the XML data contained by the specified System.String
            /// </summary>
            /// <typeparam name="T">The type of System.Object to be deserialized</typeparam>
            /// <param name="s">The System.String containing XML data</param>
            /// <returns>The System.Object being deserialized.</returns>
            public static T XmlDeserialize<T>(this string s)
            {
                var locker = new object();
                var stringReader = new StringReader(s);
                var reader = new XmlTextReader(stringReader);
                try
                {
                    var xmlSerializer = new XmlSerializer(typeof(T));
                    lock (locker)
                    {
                        var item = (T)xmlSerializer.Deserialize(reader);
                        reader.Close();
                        return item;
                    }
                }
                catch
                {
                    return default(T);
                }
                finally
                {
                    reader.Close();
                }
            }
        }
    

    这应该让您有一个良好的开端。祝你好运。

    【讨论】:

    • 为确定我的要求并编写示例代码点赞。如果有多个根使用相同的属性,是否可以说 [XmlRoot("tx")], [XmlRoot("rx")] 表示在执行时可以为任一根引用相同的类反序列化?
    【解决方案2】:
    if(null != tNode.Attributes.GetNamedItem("repeat"))
       repeat = Convert.ToInt32(tNode.Attributes.GetNamedItem("repeat").Value);
    

    更新

    既然您知道不能获得空引用,

    最好的解决方案是编写一个类,您可以使用 XmlSerializer 将您的 xml 反序列化。

    custom serialization 上的这篇文章将帮助您入门。

    要使用 XML 序列化,您必须首先标记您的数据对象 具有指示所需 XML 映射的属性。这些 属性位于 System.Xml.Serialization 命名空间中,并且 包括以下内容:

    XmlRoot 指定 XML 文件的根元素的名称。默认情况下,XmlSerializer 将使用类的名称。这个属性 可以应用于类声明。

    • XmlElement 指示用于属性或公共变量的元素名称。默认情况下,XmlSerializer 将使用属性或公共变量的名称。
    • XmlAttribute 指示属性或公共变量应序列化为属性,而不是元素,并指定属性名称。
    • XmlEnum 配置序列化枚举值时应使用的文本。如果不使用 XmlEnum,将使用枚举常量的名称。
    • XmlIgnore 表示不应序列化属性或公共变量。

    【讨论】:

    • 我考虑过这种方法,但我想知道是否有其他方法,因为如果我需要处理很多属性,这会使我的代码看起来很糟糕。
    • 肯定有更好的方法,但我觉得如果你不能自己解决空引用;解释其他方法可能有点困难。
    • 我故意没有提到这里发布的解决方案,因为我认为单独检查每个可能属性上的空项不是一个好方法,这有点明显。我继续编辑了 OP。
    【解决方案3】:

    嗯,这是一个完全未经测试的混搭功能,YMMV。

    static class XmlNodeExtensions {
        public static T GetAttrValue(this XmlNode node, string attrName) {
            return GetAttrValue(node, attrName, default(T));
        }
        public static T GetAttrValue(this XmlNode node, string attrName, T defaultValue) {
            var attr = node.Attributes.GetNamedItem(attrName);
            if (attr != null) {
                var value = attr.Value; 
                var tT = typeof(T);       // target Type
                if (tT.IsAssignableFrom(typeof(string)))
                {
                    return (T)value;
                } else
                {
                    var converter = TypeDescriptor.GetConverter(tT);
                    if (converter.CanConvertFrom(typeof(string)))
                    {
                        return (T)converter.ConvertFrom(value);
                    } else
                    {
                        throw new InvalidOperationException("No conversion possible");
                    }
                }
            } else {
                return defaultValue;
            }
        }
    }
    

    .. 然后..

    var id = node.GetAttrValue<int>("size");
    var name = node.GetAttrValue("name", "no name!");
    var titleOrNull = node.GetAttrValue<string>("title");
    

    .. 什么的。

    【讨论】:

      猜你喜欢
      • 2018-01-17
      • 1970-01-01
      • 2015-11-03
      • 2011-05-03
      • 1970-01-01
      • 2014-09-02
      • 2015-08-21
      • 2011-02-14
      • 1970-01-01
      相关资源
      最近更新 更多