转换路径
因为生成的路径多有Propertys和item,所以我将拿到的路径做处理,得到新的路径去Json数据里找值,生成路径代码如下所示:
#region 替换掉所有的properties public string GeValuePath(string oldPath) { var newPath = oldPath.Replace("properties.", ""); //处理所有properties var lastPath = newPath.Replace(".items", "[0]"); //这里是个大坑!!!!! return lastPath; } #endregion 替换掉所有的properties
没错,很快我就发现了问题,object类型还好,Array类型就麻烦了,由于JsonSchema只是描述Json结构的,所以它无需拥有每个数组的结构,也就没有每个数组的路径:看下JsonSchema长这样:
{ "$id": "http://example.com/example.json", "type": "object", "properties": { "checked": { "$id": "/properties/checked", "type": "boolean", "title": "The Checked Schema ", "description": "Equal", "default": false, "examples": [ false ] }, "dimensions": { "$id": "/properties/dimensions", "type": "object", "properties": { "width": { "$id": "/properties/dimensions/properties/width", "type": "integer", "title": "The Width Schema ", "description": "Equal", "default": 0 }, "height": { "$id": "/properties/dimensions/properties/height", "type": "integer", "title": "The Height Schema ", "description": "Equal", "default": 0, "examples": [ 10 ] } } }, "variables": { "$id": "/properties/variables", "type": "array", //就是这里的问题,不管你有多少个元素,它只给你展示结构 "items": { "$id": "/properties/variables/items", "type": "object", "properties": { "code": { "$id": "/properties/variables/items/properties/code", "type": "string", "title": "The Code Schema ", "description": "Equal", "default": "", "examples": [ "id" ] }, "name": { "$id": "/properties/variables/items/properties/name", "type": "string", "title": "The Name Schema ", "description": "Equal", "default": "", "examples": [ "业务实体数据Id" ] }, "dataType": { "$id": "/properties/variables/items/properties/dataType", "type": "string", "title": "The Datatype Schema ", "default": "", "examples": [ "String" ] }, "defaultValue": { "$id": "/properties/variables/items/properties/defaultValue", "type": "string", "title": "The Defaultvalue Schema ", "description": "Equal", "default": "", "examples": [ "" ] }, "showFlag": { "$id": "/properties/variables/items/properties/showFlag", "type": "string", "title": "The Showflag Schema ", "description": "Equal", "default": "", "examples": [ "0" ] }, "showValue": { "$id": "/properties/variables/items/properties/showValue", "type": "string", "title": "The Showvalue Schema ", "description": "Equal", "default": "", "examples": [ "" ] } } } }, "id": { "$id": "/properties/id", "type": "integer", "title": "The Id Schema ", "description": "Mapping", "default": 0, "examples": [ 1 ] }, "name": { "$id": "/properties/name", "type": "string", "title": "The Name Schema ", "description": "Equal", "default": "", "examples": [ "A green door" ] }, "price": { "$id": "/properties/price", "type": "number", "title": "The Price Schema ", "description": "Equal", "default": 0, "examples": [ 12.5 ] }, "tags": { "$id": "/properties/tags", "type": "array", "items": { "$id": "/properties/tags/items", "type": "string", "title": "The 0th Schema ", "default": "", "examples": [ "home", "green" ] } } } }
由于结构使我拿不到其它数组元素的路径,我也就拿不到所有数组元素的节点的值,我想了很多,甚至递归判断共有多少个节点,和武哥讨论后,他给出了一个方案,就是拿大的装小的,现在json数据比schema大,那么可以先获取数据的节点路径和值,再将节点路径简化为schema的路径(也就是把各种索引去掉),这样就可以拿到规则。
最佳实践
采用大装小的战略后,事情解决起来很容易,事实证明,换个角度思考,可能问题就解决掉了。
初始化Json数据字典
初始化数据的<path,value>字典
#region 递归JsonValue树获取所有路径-值字典 public void JsonValueTraverse(JToken json, ref Dictionary<string, string> jsonDic) { if (!json.HasValues) //如果json没有子节点了,说明已经到了叶子节点,该完整路径为叶子节点path { var value = json.Value<object>().ToString(); if (!jsonDic.ContainsKey(json.Path)) //如果已经存在了,则不需要重复添加 { jsonDic.Add(json.Path, value); } else { logger.Error("添加了重复节点,节点路径为" + json.Path + "节点值为" + value); } } foreach (var item in json.Children()) { JsonValueTraverse(item, ref jsonDic); //递归选取子节点 } } #endregion 递归JsonValue树获取所有路径-值字典
转换为schema路径
将数据路径转换为Schema路径
#region 转换为schema路径 public string GetRulePath(string oldPath) { var newPath = oldPath.Replace(".", ".properties."); //处理所有properties var expr = @"\[\d+\]"; var lastPath = Regex.Replace(newPath, expr, ".items"); //用正则替换所有索引 return lastPath; } #endregion 转换为schema路径
得到最终字典
代码清单:
#region 初始化路径-(值,规则)字典 /// <summary> /// 初始化字典 /// </summary> /// <param name="json"></param> /// <param name="schema"></param> /// <param name="type">0表示json数据,1表示xml数据</param> /// <returns></returns> public Dictionary<string, JsonSchemaObject> InitialJsonRule(string json, string schema, int type) { var jsonValue = JToken.Parse(json); var JsonSchema = JToken.Parse(schema); //初始化路径--规则字典 Dictionary<string, string> jsonDic = new Dictionary<string, string>(); JsonValueTraverse(jsonValue, ref jsonDic); Dictionary<string, JsonSchemaObject> ruleDic = new Dictionary<string, JsonSchemaObject>(); //规范化字典名称 foreach (var item in jsonDic) { JsonSchemaObject jso = new JsonSchemaObject(); var schemaPath = GetRulePath(item.Key); //将路径转为schemaPath去获取规则 if (type == 1) { schemaPath = GetStrAfterFirstPoint(schemaPath); //如果是xml类型的数据,还需要把路径头的名去掉 } JToken node = JsonSchema.SelectToken(schemaPath); if (node != null) { if (node.SelectToken("description") != null) { jso.rule = node.Value<string>("description");//从Json里取出规则值 } else { jso.rule = "Equal"; } jso.value = item.Value; ruleDic.Add(item.Key, jso); //只存储有规则的字段,没有规则的不管,不比较,也不关心 } } return ruleDic; } #endregion 初始化路径-(值,规则)字典
因为xml数据转为json前,需要带着最外层尖括号的名字,为了路径一致,这里我们去掉
#region xml转json需要替换头一个名 public string GetStrAfterFirstPoint(string schema) { var index = schema.IndexOf("."); var newSchema = schema.Substring(index); return newSchema; } #endregion xml转json需要替换头一个名
附送一个xml转json的方法哦:
#region xml转为json public string XmlToJson(string xmlStr) { XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlStr); return Newtonsoft.Json.JsonConvert.SerializeXmlNode(doc); } #endregion xml转为json
思考过程
一开始想着遍历JsonSchema,拿到【schema路径,规则】字典,然后再通过schem路径遍历json数据获取路径的值,结果遇到数组的情况就不能取到正确的值了,这就是小装不下大
发现实现不了后通过遍历数据Json,拿到【数据路径,数据值】字典,然后将数据路径转为schema路径去拿规则,这个就是大可以缩小
第一步将Schema和Json数据都存储好之后,使用上述方法读取,要知道读取之后只是简单的字符串,在尝试了反序列化字典无果之后,突发奇想,如果把Json当成颗多叉树呢?,使用递归遍历的方式不就可以拿到所有节点了么?
做完之后有些反思:解决方案性学习和原理性学习是两回事儿,譬如CLR这类的可以追更溯源,而解决方案类问题不应将过多精力投入去从基础学习,应该自顶向下,先找到大致的解决方案,然后哪里不会再查哪里,这样效率高。就像Schema这个学习,之前学习虽说出了几篇博文,但对于实质问题的解决并没有帮助,反倒是直接搜索解决方案,之后对Schema的理解更深了。对JToken 的使用也加深了。