本文介绍一下功能实现,并给出核心部分代码。
首先要定义各种结构,包括传入参数、传出结果、规则集合等,如下:
using GeoJSON.Net.Feature;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
namespace GISWebService.HttpRequestContent
{
/// <summary>
/// 属性质检的传入参数
/// </summary>
public class CheckFields
{
/// <summary>
/// 图层
/// </summary>
[JsonProperty("layer")]
public string Layer { get; set; }
/// <summary>
/// 要素集合
/// </summary>
[JsonProperty("feature")]
public List<Feature> Feature { get; set; }
}
/// <summary>
/// 要素属性质检结果
/// </summary>
public class CheckFieldsResult
{
/// <summary>
/// OBJECTID
/// </summary>
[JsonProperty("objectid")]
public int ObjectID { get; set; }
/// <summary>
/// 字段检查结果
/// </summary>
[JsonProperty("fields")]
public List<string> FieldInfo { get; set; }
}
/// <summary>
/// 属性质检结果
/// </summary>
public class CheckFieldsResponseBody
{
/// <summary>
/// 图层
/// </summary>
[JsonProperty("layer")]
public string Layer { get; set; }
/// <summary>
/// 质检结果
/// </summary>
[JsonProperty("info")]
public List<CheckFieldsResult> Information { get; set; }
}
/// <summary>
/// 规则:来自集合
/// </summary>
public class RuleCollection
{
/// <summary>
/// 分隔符
/// </summary>
[JsonProperty("seperator")]
[DefaultValue(",")]
public string Seperator { get; set; }
/// <summary>
/// 集合
/// </summary>
[JsonProperty("list")]
[JsonConverter(typeof(ValueToList))]
public List<string> Items { get; set; }
}
/// <summary>
/// 属性值质检规则
/// </summary>
public class RuleValueItem
{
/// <summary>
/// 字段名
/// </summary>
[JsonProperty("name")]
public string FieldName { get; set; }
/// <summary>
/// 允许空值(默认为true)
/// </summary>
[JsonProperty("nullable")]
[DefaultValue(true)]
public bool Nullable { get; set; }
/// <summary>
/// 来自集合
/// </summary>
[JsonProperty("collection", NullValueHandling = NullValueHandling.Ignore)]
public RuleCollection RuleOfCollection { get; set; }
/// <summary>
/// 正则表达式
/// </summary>
[JsonProperty("expression", NullValueHandling = NullValueHandling.Ignore)]
public string RegExp { get; set; }
}
/// <summary>
/// 要素类属性值质检规则
/// </summary>
public class RuleValue
{
/// <summary>
/// 要素类集合
/// </summary>
[JsonProperty("featureclass")]
public List<string> FeatureClassNames { get; set; }
/// <summary>
/// 各字段规则
/// </summary>
[JsonProperty("rule")]
public List<RuleValueItem> RuleItems { get; set; }
}
/// <summary>
/// 属性空值限制质检规则
/// </summary>
public class RuleRestrictionItem
{
/// <summary>
/// 主干(非空时要求分支不为空)
/// </summary>
[JsonProperty("trunk")]
public string Trunk { get; set; }
/// <summary>
/// 分支
/// </summary>
[JsonProperty("branch")]
[DefaultValue(true)]
public List<string> Branches { get; set; }
}
/// <summary>
/// 要素类属性值质检规则
/// </summary>
public class RuleRestriction
{
/// <summary>
/// 要素类集合
/// </summary>
[JsonProperty("featureclass")]
public List<string> FeatureClassNames { get; set; }
/// <summary>
/// 属性空值限制质检规则
/// </summary>
[JsonProperty("rule")]
public List<RuleRestrictionItem> RuleItems { get; set; }
}
/// <summary>
/// 属性空值限制质检规则
/// </summary>
public class RuleRelationItem
{
/// <summary>
/// 较短的字段
/// </summary>
[JsonProperty("short")]
public string ShortField { get; set; }
/// <summary>
/// 较长的字段(非空时,较短字段为其取值的一部分)
/// </summary>
[JsonProperty("long")]
public string LongField { get; set; }
/// <summary>
/// 起始索引(从1起算,取值包含该索引)
/// </summary>
[JsonProperty("from")]
public int FromIndex { get; set; }
/// <summary>
/// 终止索引(从1起算,大于起始索引,但小于总长度,取值包含该索引)
/// </summary>
[JsonProperty("to")]
public int ToIndex { get; set; }
}
/// <summary>
/// 要素类属性关系质检规则
/// </summary>
public class RuleRelation
{
/// <summary>
/// 要素类集合
/// </summary>
[JsonProperty("featureclass")]
public List<string> FeatureClassNames { get; set; }
/// <summary>
/// 属性关系质检规则
/// </summary>
[JsonProperty("rule")]
public List<RuleRelationItem> RuleItems { get; set; }
}
public class ValueToList : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
bool isTypeNullable = IsNullableType(objectType);
Type t = isTypeNullable ? Nullable.GetUnderlyingType(objectType) : objectType;
if (reader.TokenType == JsonToken.Null)
{
if (!IsNullableType(objectType))
throw new Exception(string.Format("不能转换null value to {0}.", objectType));
return null;
}
try
{
List<string> ls = new List<string>();
if (reader.TokenType == JsonToken.String)
{
string sValue = reader.Value.ToString();
string[] arrayValue = sValue.Split(',');
foreach (string s in arrayValue)
{
if (string.IsNullOrWhiteSpace(s))
continue;
ls.Add(s);
}
}
return ls;
}
catch (Exception ex)
{
throw new Exception(string.Format("Error converting value {0} to type '{1}'", reader.Value, objectType));
}
throw new Exception(string.Format("Unexpected token {0} when parsing enum", reader.TokenType));
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (null == value)
{
writer.WriteNull();
return;
}
List<string> ls = value as List<string>;
if (null == ls || 0 == ls.Count)
{
writer.WriteNull();
return;
}
string result = "";
foreach (string s in ls)
{
if (string.IsNullOrEmpty(s) || string.IsNullOrWhiteSpace(s))
continue;
if (string.IsNullOrEmpty(result))
{
result = s;
}
else
{
result += "," + s;
}
}
writer.WriteValue(result);
}
public bool IsNullableType(Type t)
{
if (t == null)
{
throw new ArgumentNullException("t");
}
return (t.BaseType.FullName == "System.ValueType" && t.GetGenericTypeDefinition() == typeof(Nullable<>));
}
}
}
先定义一个CheckValue类,来实现属性质检规则的加载:
using GeoJSON.Net.Feature;
using GISWebService.HttpRequestContent;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace GISWebService.Common
{
public class CheckValue
{
private static string _CheckRelationConfig = "CheckRelation.json";
private static string _CheckRestrictionConfig = "CheckRestriction.json";
private static string _CheckValueConfig = "CheckValue.json";
private static List<RuleRelation> _RuleRelations = null;
private static List<RuleRestriction> _RuleRestrictions = null;
private static List<RuleValue> _RuleValues = null;
/// <summary>
/// 属性关系检查规则
/// </summary>
public static List<RuleRelation> RuleRelations { get { return GetRules<RuleRelation>(_CheckRelationConfig, _RuleRelations as System.Collections.IList) as List<RuleRelation>; } }
/// <summary>
/// 属性空值约束检查规则
/// </summary>
public static List<RuleRestriction> RuleRestrictions { get { return GetRules<RuleRestriction>(_CheckRestrictionConfig, _RuleRestrictions as System.Collections.IList) as List<RuleRestriction>; } }
/// <summary>
/// 属性值检查规则
/// </summary>
public static List<RuleValue> RuleValues { get { return GetRules<RuleValue>(_CheckValueConfig, _RuleValues as System.Collections.IList) as List<RuleValue>; } }
private static System.Collections.IList GetRules<T>(string name, System.Collections.IList rules)
{
if (null == rules)
{
Type aType = typeof(List<>).MakeGenericType(typeof(T));
rules = Activator.CreateInstance(aType) as System.Collections.IList;
}
if (0 == rules.Count)
rules = LoadRules<T>(name);
return rules;
}
private static string GetConfigFile(string name)
{
string path = "";
try
{
string s = System.IO.Path.Combine(AppSettings.PathByKey("tempdir"), name);
path = System.IO.File.Exists(s) ? s : "";
}
catch (Exception e)
{
ExceptionHelper.LogActionException(e);
}
return path;
}
private static System.Collections.IList LoadRules<T>(string name)
{
string sPath = GetConfigFile(name);
Type aType = typeof(List<>).MakeGenericType(typeof(T));
System.Collections.IList ls = Activator.CreateInstance(aType) as System.Collections.IList;
if (!string.IsNullOrEmpty(sPath))
{
string sContent = System.IO.File.ReadAllText(sPath, System.Text.Encoding.Default);
ls = Newtonsoft.Json.JsonConvert.DeserializeObject(sContent, aType) as System.Collections.IList;
}
return ls;
}
}
}
在核心的质检类CheckValueHelper类中定义质检的方法,并让它继承自CheckValue类:
using GeoJSON.Net.Feature;
using GISWebService.HttpRequestContent;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
namespace GISWebService.Common
{
public class CheckValueHelper : CheckValue
{
public static CheckFieldsResult Check(string featureclass, Feature feature)
{
CheckFieldsResult result = new CheckFieldsResult() { ObjectID = -1, FieldInfo = new List<string>() };
int oid = GetObjectID(feature);
if (0 <= oid)
{
result.ObjectID = oid;
result.FieldInfo.AddRange(CheckRelation(featureclass, feature.Properties, RuleRelations));
result.FieldInfo.AddRange(CheckRestriction(featureclass, feature.Properties, RuleRestrictions));
result.FieldInfo.AddRange(CheckValue(featureclass, feature.Properties, RuleValues));
}
return result;
}
private static int GetObjectID(Feature feature)
{
int n = -1;
if (null == feature || null == feature.Properties || 0 == feature.Properties.Count)
return n;
foreach (var item in feature.Properties)
{
if (0 == string.Compare(item.Key, "objectid", true))
{
if (null == item.Value)
continue;
int oid = -1;
if (int.TryParse(item.Value.ToString(), out oid) && 0 < oid)
{
n = Convert.ToInt32(item.Value.ToString());
break;
}
}
}
return n;
}
private static string GetValueToString(Dictionary<string, object> dict, string key, bool ignoreCase = true, bool removeWhitespace = true)
{
string s = "";
if (null == dict || 0 == dict.Count || string.IsNullOrEmpty(key))
return s;
foreach (var item in dict)
{
if (0 == string.Compare(item.Key, key, ignoreCase) && item.Value != null)
{
s = removeWhitespace ? item.Value.ToString().Replace(" ", "") : item.Value.ToString();
break;
}
}
return s;
}
private static List<string> CheckRelation(string featureclass, Dictionary<string, object> dict, List<RuleRelation> lsRule)
{
int nA = -1;
for (int n = 0; n < lsRule.Count; n++)
{
int m = lsRule[n].FeatureClassNames.FindIndex((e) => { return 0 == string.Compare(e, featureclass, true); });
if (m >= 0)
{
nA = n;
break;
}
}
if (nA < 0)
return null;
List<string> ls = new List<string>();
List<RuleRelationItem> lsRuleItem = lsRule[nA].RuleItems;
foreach (var item in lsRuleItem)
{
if (string.IsNullOrEmpty(item.ShortField) || string.IsNullOrEmpty(item.LongField) || item.FromIndex <= 0 || item.ToIndex <= 0 || item.FromIndex > item.ToIndex)
{
Log.Loging.Info(string.Format("关联检查配置文件有误。Long = {0}, Short = {1}, From = {2} ,To = {3}", item.LongField, item.ShortField, item.FromIndex, item.ToIndex));
continue;
}
string sShortLengthValue = GetValueToString(dict, item.ShortField);
string sLongLengthValue = GetValueToString(dict, item.LongField);
if (string.IsNullOrEmpty(sShortLengthValue) || string.IsNullOrEmpty(sLongLengthValue))
{
Log.Loging.Info(string.Format("有数据为空。{0} = {1}, {2} = {3}", item.ShortField, sShortLengthValue, item.LongField, sLongLengthValue));
continue;
}
if (sLongLengthValue.Length < item.ToIndex)
{
Log.Loging.Info(string.Format("数据有误,{0} 字段取值 {1} 未能超过索引长度 {2}", item.LongField, sLongLengthValue, item.ToIndex));
continue;
}
string sSubString = sLongLengthValue.Substring(item.FromIndex - 1, item.ToIndex - item.FromIndex + 1);
if (0 != string.Compare(sShortLengthValue, sSubString, true))
{
if (item.FromIndex == item.ToIndex)
ls.Add(string.Format("字段 {0} 取值不为字段 {1} 取值的第 {2} 位", item.ShortField, item.LongField, item.FromIndex));
else
ls.Add(string.Format("字段 {0} 取值不为字段 {1} 取值的第 {2} 位至第 {3} 位", item.ShortField, item.LongField, item.FromIndex, item.ToIndex));
}
}
return ls;
}
private static List<string> CheckRestriction(string featureclass, Dictionary<string, object> dict, List<RuleRestriction> lsRule)
{
int nA = -1;
for (int n = 0; n < lsRule.Count; n++)
{
int m = lsRule[n].FeatureClassNames.FindIndex((e) => { return 0 == string.Compare(e, featureclass, true); });
if (m >= 0)
{
nA = n;
break;
}
}
if (nA < 0)
return null;
List<string> ls = new List<string>();
List<RuleRestrictionItem> lsRuleItem = lsRule[nA].RuleItems;
foreach (var item in lsRuleItem)
{
if (string.IsNullOrEmpty(item.Trunk) || null == item.Branches || 0 == item.Branches.Count)
{
Log.Loging.Info("主干字段配置有误");
continue;
}
string sTrunkFieldValue = GetValueToString(dict, item.Trunk);
if (string.IsNullOrEmpty(sTrunkFieldValue))
{
Log.Loging.Info(string.Format("不包含字段 {0} 或取值为空", item.Trunk));
continue;
}
foreach (var branch in item.Branches)
{
if (string.IsNullOrEmpty(branch))
{
Log.Loging.Info("分支字段配置有误");
continue;
}
string sBrunchFieldValue = GetValueToString(dict, branch);
if (string.IsNullOrEmpty(sBrunchFieldValue))
ls.Add(string.Format("字段 {0} 不为空时要求字段 {1} 也不能为空", item.Trunk, branch));
}
}
return ls;
}
private static List<string> CheckValue(string featureclass, Dictionary<string, object> dict, List<RuleValue> lsRule)
{
int nA = -1;
for (int n = 0; n < lsRule.Count; n++)
{
int m = lsRule[n].FeatureClassNames.FindIndex((e) => { return 0 == string.Compare(e, featureclass, true); });
if (m >= 0)
{
nA = n;
break;
}
}
if (nA < 0)
return null;
List<string> ls = new List<string>();
List<RuleValueItem> lsRuleItem = lsRule[nA].RuleItems;
foreach (var aPair in dict)
{
RuleValueItem fieldRule = null;
for (int k = 0; k < lsRuleItem.Count; k++)
{
if (0 == string.Compare(lsRuleItem[k].FieldName, aPair.Key, true))
{
fieldRule = lsRuleItem[k];
break;
}
}
if (null == fieldRule)
continue;
string sValue = "";
if (null != aPair.Value)
sValue = aPair.Value.ToString().Replace(" ", "");
if (!fieldRule.Nullable)
{
if (string.IsNullOrEmpty(sValue))
{
ls.Add(string.Format("字段 {0} 取值不允许非空", aPair.Key));
continue;
}
}
if (!string.IsNullOrEmpty(sValue) && null != fieldRule.RuleOfCollection)
{
if (null != fieldRule.RuleOfCollection.Items && 0 < fieldRule.RuleOfCollection.Items.Count && !string.IsNullOrEmpty(fieldRule.RuleOfCollection.Seperator))
{
char[] cSeperator = fieldRule.RuleOfCollection.Seperator.ToCharArray();
string[] arrayValue = sValue.Split(cSeperator, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in arrayValue)
{
if (0 > fieldRule.RuleOfCollection.Items.FindIndex(e => { return 0 == string.Compare(item, e, true); }))
{
ls.Add(string.Format("字段 {0} 取值不在指定的集合范围内", aPair.Key));
break;
}
}
}
}
if (!string.IsNullOrEmpty(sValue) && !string.IsNullOrEmpty(fieldRule.RegExp))
{
if (!(new Regex(fieldRule.RegExp).IsMatch(sValue)))
ls.Add(string.Format("字段 {0} 取值不符合规范", aPair.Key));
}
}
return ls;
}
}
}
调用的地方,即接口处实现如下:
/// <summary>
/// 属性质检(可配置式)
/// </summary>
/// <returns>质检结果</returns>
/// <status>可用</status>
[HttpPost]
[ResponseType(typeof(List<CheckFieldsResponseBody>))]
public IHttpActionResult CheckFields2([FromBody]List<CheckFields> param)
{
try
{
List<CheckFieldsResponseBody> lsResult = new List<CheckFieldsResponseBody>() { };
foreach (var item in param)
{
CheckFieldsResponseBody aResponse = new CheckFieldsResponseBody() { Layer = item.Layer, Information = new List<CheckFieldsResult>() };
foreach (var feature in item.Feature)
aResponse.Information.Add(CheckValueHelper.Check(aResponse.Layer, feature));
lsResult.Add(aResponse);
}
return Json(new JObject(new JProperty("status", 0), new JProperty("result", JToken.FromObject(lsResult))));
}
catch (Exception e)
{
ExceptionHelper.LogActionException(e);
return Json(new ResponseInfo() { StatusCode = 1, Message = e.Message });
}
}