简体   繁体   English

解析XML属性的最佳方法

[英]Best way to parse XML Attributes

I have the following XML document that needs to be parsed: 我有以下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>
...

I wrote the following code for parsing: 我编写了以下代码进行解析:

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);
}

The code throws a nullreferenceexception if I try to obtain the value of a certian attribute that doesn't exist (IE: tNode.Attribute.GetNamedItem("repeat").value fails on all nodes that doesn't have the repeat attribute). 如果我尝试获取不存在的certian属性的值,则代码将引发nullreferenceexception(IE:tNode.Attribute.GetNamedItem(“ repeat”)。value在所有没有重复属性的节点上都失败)。 What is a way that I can verify if a certain attribute exists? 我可以通过什么方法来验证某个属性是否存在?

Also the above code isn't clean at all. 上面的代码也不是干净的。 What is the best way to organize the above code? 整理以上代码的最佳方法是什么?

Edit: I am aware of the approach where you can individually check whether the attributes are null or not before getting the values off them but this makes the code look very dirty as I am required to write a lot of ifs (or nested ifs) 编辑:我知道一种方法,您可以在获取值之前分别检查属性是否为null,但这会使代码看起来很脏,因为我需要编写大量的if(或嵌套的if)

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

This becomes problematic in the long run if I have to write a lot more attributes. 从长远来看,如果我不得不编写更多的属性,这将成为问题。 I'd like to know more of an organized approach for this (Perhaps XML Attributes can be enumerated? I don't know.) 我想更多地了解一种有组织的方法(也许可以枚举XML属性?我不知道。)

if(null != tNode.Attributes.GetNamedItem("repeat"))
   repeat = Convert.ToInt32(tNode.Attributes.GetNamedItem("repeat").Value);

Update 更新

Now that you know not to get a null reference, 现在您知道不会获得空引用,

The best solution to this would be to write a class into which you would deserialize your xml using the XmlSerializer . 最好的解决方案是编写一个类,使用XmlSerializer将xml反序列化到该类中。

This article on custom serialization will get you started. 有关自定义序列化的本文将帮助您入门。

To use XML serialization, you must first mark up your data objects with attributes that indicate the desired XML mapping. 要使用XML序列化,您必须首先使用指示所需XML映射的属性标记数据对象。 These attributes are found in the System.Xml.Serialization namespace and include the following: 这些属性在System.Xml.Serialization命名空间中找到,包括以下内容:

XmlRoot Specifies the name of the root element of the XML file. XmlRoot指定XML文件的根元素的名称。 By default, XmlSerializer will use the name of the class. 默认情况下,XmlSerializer将使用该类的名称。 This attribute can be applied to the class declaration. 该属性可以应用于类声明。

  • XmlElementIndicates the element name to use for a property or public variable. XmlElement指示用于属性或公共变量的元素名称。 By default, XmlSerializer will use the name of the property or public variable. 默认情况下,XmlSerializer将使用属性或公共变量的名称。
  • XmlAttributeIndicates that a property or public variable should be serialized as an attribute, not an element, and specifies the attribute name. XmlAttribute指示应将属性或公共变量序列化为属性而不是元素,并指定属性名称。
  • XmlEnumConfigures the text that should be used when serializing enumerated values. XmlEnum配置序列化枚举值时应使用的文本。 If you don't use XmlEnum, the name of the enumerated constant will be used. 如果您不使用XmlEnum,则将使用枚举常量的名称。
  • XmlIgnoreIndicates that a property or public variable should not be serialized. XmlIgnore指示不应序列化属性或公共变量。

Agree with @nunespascal and here is the code I prepared for you already .. he answered quicker than me.. LOL: 同意@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; }
    }

here is my helper class for deserializing: 这是我的反序列化的帮助器类:

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();
            }
        }
    }

That should get you off to a good start. 那应该使您有个良好的开端。 Good luck. 祝好运。

Well, here is an entirely untested mashup-function, YMMV. 好吧,这是一个完全未经测试的混搭功能,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;
        }
    }
}

.. then .. .. 然后 ..

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

.. or something. .. 或者其他的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM