zoukankan      html  css  js  c++  java
  • IEnumerable<IEnumerable<string>>结构解析通用解决方案(支持指定属性顺序)

    一、前言

    类似如下字符串

     "ID", "NameValue", "CodeValue", "ExchangeTypeValue", 6, "invalid"

     "ID2", "NameValue2", "CodeValue2", "ExchangeTypeValue2", 6, "invalid"

    .......

    有可能是文件中存在的,或者调用其他程序返回的结构化数据,那么该如何解析?当其他场景中,只是返回顺序(属性顺序)变了,类结构还是一样,又如何应对?当有很多类似场景时,是不是该抽象出一个泛型方法来应对该场景?当然,也不仅仅于上述情况,可能返回的结构是确定,只是形式不一样,这个过程这里暂时省略,因为正则表达式完全能够解析出来。要用以下的方法,必须转换成IEnumerable<IEnumerable<string>>结构,IEnumerable<IEnumerable<string>>结构中IEnumerable<string>为一个对象所有的值,总体是多个对象的值集合。本文中用反射写的(关于IL操作的后续文章提供),相关的类图如下:

    二、ResultTransfer的具体实现

    ResultTransfer主要用于对IEnumerable<IEnumerable<string>>结构的解析,另外还可以指定params string[] propertyNames属性参数列表来确定解析顺序(也即是属性顺序),主要方法如下:

         public static IList<T> Parse<T>(IEnumerable<IEnumerable<string>> entityRows, params string[] propertyNames) where T : new()

         第一个参数entityRows为对象列表值集合。

         第二个参数propertyNames为可选参数,输入该参数后,如果propertyNames中存在相关属性,则按照propertyNames对应的属性顺序进行解析。否则按照提供的T类中属性的DataMemberAttribute来确定属性顺序进行解析。

    实现代码非常简洁和简单,方法具体如下所示:

            public static IList<T> Parse<T>(IEnumerable<IEnumerable<string>> entityRows, params string[] propertyNames) where T : new()
            {
                if (entityRows == null || entityRows.Count() == 0)
                {
                    return new List<T>();
                }
    
                IList<T> entities = new List<T>();
                var members = new DataMemberAttributeCollection(typeof(T), propertyNames);
    
                if (members.Count <= 1)
                {
                    return new List<T>();
                }
    
                FuncProvider funcProvider = new FuncProvider();
    
                foreach (var propertyValues in entityRows)
                {
                    if (propertyValues == null || propertyValues.Count() == 0)
                    {
                        continue;
                    }
    
                    entities.Add(Generate<T>(propertyValues, members, funcProvider));
                }
    
                return entities;
            }
    
            private static T Generate<T>(IEnumerable<string> propertyValues, DataMemberAttributeCollection members,
                FuncProvider funcProvider) where T : new()
            {
                T entity = Activator.CreateInstance<T>();
                int memberCount = members.Count;
                int propertyCount = propertyValues.Count();
    
                if (memberCount == 0 || propertyCount == 0)
                {
                    return entity;
                }
    
                int convertCount = Math.Min(memberCount, propertyCount);
                DataMemberAttribute currAttribute;
                PropertyInfo currPropertyInfo;
    
                int propertyValueIndex = 0;
    
                foreach (string propertyValue in propertyValues)
                {
                    if (propertyValueIndex >= convertCount)
                    {
                        break;
                    }
    
                    propertyValueIndex++;
                    currAttribute = members[propertyValueIndex - 1];
                    currPropertyInfo = currAttribute.PropertyInfo;
    
                    if (propertyValue == null)
                    {
                        currPropertyInfo.SetValue(entity, null, null);
                        continue;
                    }
    
                    if (propertyValue.GetType() == currAttribute.PropertyType)
                    {
                        currPropertyInfo.SetValue(entity, propertyValue, null);
                    }
                    else
                    {
                        object result = funcProvider.DynamicInvoke(currAttribute.PropertyType, (propertyValue ?? string.Empty).ToString());
                        currPropertyInfo.SetValue(entity, result, null);
                    }
                }
    
                return entity;
            }

    三、DataMemberAttributeCollection的具体实现

    DataMemberAttributeCollection集合类主要用于设置解析属性的顺序,同样,该类提供二个参数的构造函数用于生成相应的配置信息public DataMemberAttributeCollection(Type type, params string[] propertyNames)。

    主要代码如下:

            public void GetConfiguration(Type type, params string[] propertyNames)
            {
                if (type == null || type.GetProperties().Length <= 0)
                {
                    return;
                }
    
                if (propertyNames == null || propertyNames.Length == 0)
                {
                    AddAllDataMemberAttributes(type);
                }
                else
                {
                    AddDataMemberAttributes(type, propertyNames);
                }
    
                this._memberAttributes = this._memberAttributes.OrderBy(p => p.Order).ToList();
            }
    
            private void AddDataMemberAttributes(Type type, string[] propertyNames)
            {
                IList<PropertyInfo> validPropertyInfos = new List<PropertyInfo>();
                PropertyInfo tempPropertyInfo;
    
                foreach (string propertyName in propertyNames)
                {
                    if (string.IsNullOrWhiteSpace(propertyName))
                    {
                        continue;
                    }
    
                    tempPropertyInfo = type.GetProperty(propertyName.Trim());
    
                    if (tempPropertyInfo == null)
                    {
                        throw new ArgumentException(string.Format(@"Contains Invalid Property Name Arg : {0}.", propertyName.Trim()));
                    }
    
                    validPropertyInfos.Add(tempPropertyInfo);
                }
    
                if (validPropertyInfos.Count() > 0)
                {
                    foreach (var property in validPropertyInfos)
                    {
                        AddAttributes(new DataMemberAttribute(), property);
                    }
                }
            }
    
            private void AddAllDataMemberAttributes(Type type)
            {
                DataMemberAttribute attr = null;
                foreach (PropertyInfo propertyInfo in type.GetProperties())
                {
                    attr = AttributeUtility.GetCustomAttribute<DataMemberAttribute>(propertyInfo);
    
                    if (attr == null)
                    {
                        continue;
                    }
    
                    if (!attr.IsRequire)
                    {
                        continue;
                    }
    
                    if (this._memberAttributes.Count(p => p.Order == attr.Order) > 0)
                    {
                        throw new ArgumentException(string.Format(@"Contains Same Order {0}.Please Look Up DataMemberAttribute
                                Of The Type {1}", attr.Order, type.Name));
                    }
    
                    AddAttributes(attr, propertyInfo);
                }
            }
    
            private void AddAttributes(DataMemberAttribute attr, PropertyInfo propertyInfo)
            {
                if (string.IsNullOrWhiteSpace(attr.Name))
                {
                    attr.Name = propertyInfo.Name;
                }
    
                attr.PropertyName = propertyInfo.Name;
                attr.PropertyType = propertyInfo.PropertyType;
                attr.PropertyInfo = propertyInfo;
    
                this._memberAttributes.Add(attr);
            }

    该类确保指定Type的类中DataMemberAttribute是否设置正确(是否有相同的Order),确保是否输入了错误的propertyName。

    四、具体应用

    对于具体应用的话,用单元测试来得方便与直接。

    (1)对于只输入一个参数的应用如下:

            [TestMethod()]
            public void ParseTest()
            {
                IList<IList<string>> entityRows = new List<IList<string>>();
                entityRows.Add(new List<string>() { "3", "NameValue", "CodeValue", "ExchangeTypeValue", "6", "invalid" });
    
                var contracts = ResultTransfer.Parse<ContinousContract>(entityRows);
    
                Assert.IsNotNull(contracts);
                Assert.IsTrue(contracts.Count == 1);
                Assert.AreEqual(contracts[0].Code, "CodeValue");
                Assert.AreEqual(contracts[0].Name, "NameValue");
                Assert.AreEqual(contracts[0].ExchangeType, "ExchangeTypeValue");
                Assert.AreEqual(contracts[0].OrgidID, 3);
                Assert.AreEqual(contracts[0].ExchangeTypeValue, 6);
            }

    (2)对于只输入无效参数的应用如下:

            [TestMethod()]
            public void ParseWithInvalidArgTest()
            {
                IList<IList<string>> entityRows = new List<IList<string>>();
                entityRows.Add(new List<string>() { "sss", "NameValue", "CodeValue", "ExchangeTypeValue", "6", "invalid" });
    
                var contracts = ResultTransfer.Parse<ContinousContract>(entityRows);
    
                Assert.IsNotNull(contracts);
                Assert.IsTrue(contracts.Count == 1);
                Assert.AreEqual(contracts[0].Code, "CodeValue");
                Assert.AreEqual(contracts[0].Name, "NameValue");
                Assert.AreEqual(contracts[0].ExchangeType, "ExchangeTypeValue");
                Assert.AreEqual(contracts[0].OrgidID, 0);
                Assert.AreEqual(contracts[0].ExchangeTypeValue, 6);
            }

    输入无效的IEnumerable<IEnumerable<string>>参数,方法内部会进行隐式的转换,比如“sss”转换成0。

    (3)对于二个参数时的应用如下:

            [TestMethod()]
            public void ParseWithArgTest()
            {
                IList<IList<string>> entityRows = new List<IList<string>>();
                entityRows.Add(new List<string>() { "3", "NameValue", "ExchangeTypeValue", "6", "invalid" });
                var propertyNames = new List<string>() { "ExchangeTypeValue", "Name", "", "ExchangeType" };
    
                var contracts = ResultTransfer.Parse<ContinousContract>(entityRows, propertyNames.ToArray());
    
                Assert.IsNotNull(contracts);
                Assert.IsTrue(contracts.Count == 1);
                Assert.AreEqual(contracts[0].Code, null);
                Assert.AreEqual(contracts[0].Name, "NameValue");
                Assert.AreEqual(contracts[0].ExchangeType, "ExchangeTypeValue");
                Assert.AreEqual(contracts[0].OrgidID, 0);
                Assert.AreEqual(contracts[0].ExchangeTypeValue, 3);
            }

    一旦输入二个参数,且propertyNames参数的个数大于0,则以propertyNames对应的属性顺序进行解析。对于输入错误的属性名,方法内部会抛出异常,当然也可以增加一个参数用于控制是否抛出异常,或者写入日志文件中等。

    对于将固定格式的字符串解析成IEnumerable<IEnumerable<string>>,正则表达式解析的话比较简单,此文不做讲解,略过...

  • 相关阅读:
    88. Merge Sorted Array
    87. Scramble String
    86. Partition List
    85. Maximal Rectangle
    84. Largest Rectangle in Histogram
    83. Remove Duplicates from Sorted List
    82. Remove Duplicates from Sorted List II
    81. Search in Rotated Sorted Array II
    80. Remove Duplicates from Sorted Array II
    计算几何——点线关系(叉积)poj2318
  • 原文地址:https://www.cnblogs.com/jasenkin/p/entity_rows_value_parse.html
Copyright © 2011-2022 走看看