验证JSON格式(长度,字段类型,必填等等)

最近接了个项目,一个中间转发系统

大概需求是根据推送系统的报文,转发给其他的系统,转发前需要验证报文的格式

需要验证如下几种异常:

2:字段类型不匹配,字段值数据类型验证、字段类型验证(字段,对象,数组)

6:字段内容不能为空,字段值非空验证

1:字段长度不合规,字段值长度验证

3:字段找不到匹配内容,即报文含有不需要的节点或者字段

5:字段不存在,即需要该节点或字段,但是实际报文没有

死写不现实

1.接口太多了

2.报文字段未最终确定,会频繁变动

3.验证规则未最终确定

所以决定规则全部动态配,验证也是统一方法处理

目前手头只有报文demo格式,就像这样(实际报文不会这么简单,会出现,数组,对象,各种嵌套)

{
  "DATA": [
    {
      "XXXX": [
        {
          "FIELD1": "字段说明1",
          "FIELD2": "字段说明2"
        }
      ]
    }
  ]
}

最终我决定,将报文节点分成array数组,map对象,field字段三种类型,field里面又分string,int,float,timestamp这几种类型

最终处理的报文验证规则如下(报文demo是怎么转换验证规则的下面会有说明)

{
  "DATA": {
    "type": "array",
    "required": "1",
    "name": "DATA",
    "memo": "",
    "childFileds": {
      "FIELD1": {
        "field_type": "string",
        "field_length": "20",//长度不超过20
        "required": "1",//必填
        "type": "field",
        "name": "FIELD1",
        "memo": "字段说明1"
      },
      "FIELD2": {
        "field_type": "string",
        "field_length": "20",
        "required": "",
        "type": "field",
        "name": "FIELD2",
        "memo": "字段说明2"
      }
    }
  }
}

剩下的就是实际报文跟这个验证规则进行验证处理了,下面贴上java代码

//返回错误信息list,string[]第一位是错误code,第二位是具体错误说明
public static List<String[]> checkDo(String params, String rules) {
            List<String[]> errors = new java.util.LinkedList<String[]>();
        try {
            if (StrKit.isBlank(rules)) {// 不需要验证
                return errors;
            }
            Map<String, Object> data = gson.fromJson(params, Map.class);
            Map<String, Map<String, Object>> checkMap = gson.fromJson(rules, Map.class);
            checkDo(data, checkMap, errors);
        } catch (Exception e) {
            e.printStackTrace();
            errors.add(new String[] {"7", e.getMessage()});
        }
        return errors;
    }

    public static void checkDo(Map<String, Object> data, Map<String, Map<String, Object>> checkMap, List<String[]> errors) {
        Set<String> keys = data.keySet();
        for(Map.Entry<String, Object> entry : data.entrySet()){
            String key = entry.getKey();
            Object val = entry.getValue();
            
            Map<String, Object> rc = checkMap.get(key);
            if (null == rc) {
                if (val instanceof String || val instanceof Number) {// 不需要的字段
                    errors.add(new String[] {"3", "字段找不到匹配内容:" + key});
                } else {// 不需要的节点
                    errors.add(new String[] {"3", "字段找不到匹配内容:" + key});
                }
                continue;
            }

            for(Entry<String, Map<String, Object>> entry2 : checkMap.entrySet()){
                String key2 = entry2.getKey();
                Map<String, Object> rcc = entry2.getValue();
                
                Integer required = getInt(rcc, "required");
                if (!keys.contains(key2) && null != required && required.intValue() == 1) {
                    errors.add(new String[] {"5", "字段不存在:" + key2});
                    continue;
                }
            }

            String v_type = getStr(rc, "type");
            switch (v_type) {
                case "field":// 字段,验证必填、长度
                    String field_type = getStr(rc, "field_type");
                    Integer required = getInt(rc, "required");
                    Integer field_length = getInt(rc, "field_length");

                    switch (field_type) {
                        case "string": // 字符串
                            if (!(null == val || val instanceof String || val instanceof Number)) {
                                // errors.add(key + ",应为一个字段,不是节点。");
                                errors.add(new String[] {"2", "字段类型不匹配:" + key});
                                continue;
                            }

                            if (null != required && required.intValue() == 1) {// 必填
                                if (null == val || "".equals(String.valueOf(val))) {
                                    // errors.add(key + "不能为空");
                                    errors.add(new String[] {"6", "字段内容不能为空:" + key});
                                    continue;
                                }
                            }
                            if (null != field_length) {
                                if (null != val && String.valueOf(val).length() > field_length) {
                                    // errors.add(key + "超长(最大" + field_length + ")," + val + "长" + String.valueOf(val).length() + "。");
                                    errors.add(new String[] {"1", "字段长度不合规:" + key + ",规定长度" + field_length});
                                    continue;
                                }
                            }
                            break;
                        case "int":// 数值
                            if (!(null == val || val instanceof String || val instanceof Number)) {
                                // errors.add(key + ",应为一个字段,不是节点。");
                                errors.add(new String[] {"2", "字段类型不匹配:" + key});
                                continue;
                            }

                            Integer v_int = null;
                            try {
                                if (null != val) {
                                    v_int = Double.valueOf(String.valueOf(val)).intValue();
                                }
                            } catch (Exception e) {
                                // errors.add(key + ",字段类型错误,应为‘int’");
                                errors.add(new String[] {"2", "字段类型不匹配:" + key});
                                continue;
                            }
                            if (null != required && required.intValue() == 1) {// 必填
                                if (null == v_int) {
                                    // errors.add(key + "不能为空");
                                    errors.add(new String[] {"6", "字段内容不能为空:" + key});
                                    continue;
                                }
                            }
                            if (null != field_length) {
                                if (null != v_int && v_int.toString().length() > field_length) {
                                    // errors.add(key + "超长(最大" + field_length + ")," + val + "长" + String.valueOf(val).length() + "。");
                                    errors.add(new String[] {"1", "字段长度不合规:" + key + ",规定长度" + field_length});
                                    continue;
                                }
                            }
                            break;
                        case "float":// float
                            if (!(null == val || val instanceof String || val instanceof Number)) {
                                // errors.add(key + ",应为一个字段,不是节点。");
                                errors.add(new String[] {"2", "字段类型不匹配:" + key});
                                continue;
                            }

                            Double v_float = null;
                            try {
                                if (null != val) {
                                    v_float = Double.valueOf(String.valueOf(val));
                                }
                            } catch (Exception e) {
                                // errors.add(key + ",字段类型错误,应为‘float’");
                                errors.add(new String[] {"2", "字段类型不匹配:" + key});
                                continue;
                            }
                            if (null != required && required.intValue() == 1) {// 必填
                                if (null == v_float) {
                                    // errors.add(key + "不能为空");
                                    errors.add(new String[] {"6", "字段内容不能为空:" + key});
                                    continue;
                                }
                            }
                            if (null != field_length) {
                                if (null != v_float && v_float.toString().length() > field_length) {
                                    // errors.add(key + "超长(最大" + field_length + ")," + val + "长" + String.valueOf(val).length() + "。");
                                    errors.add(new String[] {"1", "字段长度不合规:" + key + ",规定长度" + field_length});
                                    continue;
                                }
                            }
                            break;
                        case "timestamp":
                            if (!(null == val || val instanceof String || val instanceof Number)) {
                                // errors.add(key + ",应为一个字段,不是节点。");
                                errors.add(new String[] {"2", "字段类型不匹配:" + key});
                                continue;
                            }

                            Long v_long = null;
                            try {
                                if (null != val) {
                                    v_long = Double.valueOf(String.valueOf(val)).longValue();
                                }
                            } catch (Exception e) {
                                // errors.add(key + ",字段类型错误,应为‘timestamp’");
                                errors.add(new String[] {"2", "字段类型不匹配:" + key});
                                continue;
                            }
                            if (null != required && required.intValue() == 1) {// 必填
                                if (null == v_long) {
                                    // errors.add(key + "不能为空");
                                    errors.add(new String[] {"6", "字段内容不能为空:" + key});
                                    continue;
                                }
                            }
                            if (null != field_length) {
                                if (null != v_long && v_long.toString().length() > field_length) {
                                    // errors.add(key + "超长(最大" + field_length + ")," + val + "长" + String.valueOf(val).length() + "。");
                                    errors.add(new String[] {"1", "字段长度不合规:" + key + ",规定长度" + field_length});
                                    continue;
                                }
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case "array":// 数组
                    if (!(null == val || val instanceof Collection)) {
                        // errors.add(key + ",应为一个数组。");
                        errors.add(new String[] {"2", "字段类型不匹配:" + key});
                        continue;
                    }
                    if (null != rc.get("childFileds") && null != val) {
                        List<Map<String, Object>> ll = (List<Map<String, Object>>) val;
                        for (Map<String, Object> m : ll) {
                            checkDo(m, (Map<String, Map<String, Object>>) rc.get("childFileds"), errors);
                        }
                    }
                    break;
                case "map":// 对象
                    if (!(null == val || val instanceof Map)) {
                        // errors.add(key + ",应为一个对象。");
                        errors.add(new String[] {"2", "字段类型不匹配:" + key});
                        continue;
                    }
                    if (null != rc.get("childFileds") && null != val) {
                        checkDo((Map<String, Object>) val, (Map<String, Map<String, Object>>) rc.get("childFileds"), errors);
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

 public static final Integer getInt(Map<?, ?> map, String key) {
        Object i = map.get(key);
        if (null != i && !StrKit.isBlank(String.valueOf(i))) {
            return Double.valueOf(String.valueOf(i)).intValue();
        }
        return null;
    }

    public static String getStr(Map<?, ?> map, String key) {
        Object i = map.get(key);
        if (null != i) {
            return String.valueOf(i);
        }
        return null;
    }

 下面说明下报文规则是怎么处理的

第一步就是把报文demo处理处理成rule的,调用下面的方法返回map

//params 为报文demo,rules为已经保存的验证格式(可以为null)
public static LinkedHashMap<String, Map<String, Object>> rulesAuto(String params, String rules) {
        LinkedHashMap<String, Map<String, Object>> retMap = new LinkedHashMap<String, Map<String,Object>>();
        Map<String, Object> parm = gson.fromJson(params, LinkedHashMap.class);
        Map<String, Map<String, Object>> checkMap = null;
        if(!StrKit.isBlank(rules)) {
            checkMap = gson.fromJson(rules, Map.class);
        }
        
        for(Map.Entry<String, Object> entry : parm.entrySet()){
            String mapKey = entry.getKey();
            
            Map<String, Object> cm = null;
            if(null != checkMap) {
                cm = checkMap.get(mapKey);
            }
            
            Object mapValue = entry.getValue();
            LinkedHashMap<String, Object> th = new LinkedHashMap<String, Object>();
            //th.put("name", mapKey);
            if(null != cm) {
                th.put("memo", cm.get("memo"));
                th.put("required", getInt(cm, "required"));
            } else {
                th.put("required", 0);
            }

            if(mapValue instanceof String || mapValue instanceof Number) {//字段
                th.put("type", "field");
                if(null != cm) {
                    th.put("memo", cm.get("memo"));
                    th.put("field_type", cm.get("field_type"));
                    th.put("field_length", getInt(cm, "field_length"));
                } else {
                    th.put("memo", mapValue);
                    th.put("field_type", "string");
                    th.put("field_length", "");
                }
                retMap.put(mapKey, th);
            } else if(mapValue instanceof Collection) {//队列
                LinkedHashMap<String, Object> childFileds = new LinkedHashMap<String, Object>();
                th.put("type", "array");
                th.put("childFileds", childFileds);
                retMap.put(mapKey, th);
                rulesAutoNext(((List<Map<String, Object>>)mapValue).get(0), (null == cm || !(cm.get("childFileds") instanceof Map)) ? null : (Map<String, Map<String,Object>>)cm.get("childFileds"), childFileds);
            } else {//map
                LinkedHashMap<String, Object> childFileds = new LinkedHashMap<String, Object>();
                th.put("type", "map");
                th.put("childFileds", childFileds);
                retMap.put(mapKey, th);
                rulesAutoNext((Map<String, Object>)mapValue, (null == cm || !(cm.get("childFileds") instanceof Map)) ? null : (Map<String, Map<String,Object>>)cm.get("childFileds"), childFileds);
            }
        }
        return retMap;
    }
    
    public static void rulesAutoNext(Map<String, Object> parm, Map<String, Map<String, Object>> checkMap, LinkedHashMap<String, Object> retMap) {
        for(Map.Entry<String, Object> entry : parm.entrySet()){
            String mapKey = entry.getKey();
            Object mapValue = entry.getValue();
            
            Map<String, Object> cm = null;
            if(null != checkMap) {
                cm = checkMap.get(mapKey);
            }
            
            LinkedHashMap<String, Object> th = new LinkedHashMap<String, Object>();
            //th.put("name", mapKey);
            //th.put("memo", mapKey);
            if(null != cm) {
                th.put("memo", cm.get("memo"));
                th.put("required", getInt(cm, "required"));
            } else {
                th.put("required", 0);
            }

            if(mapValue instanceof String || mapValue instanceof Number) {//字段
                th.put("type", "field");
                if(null != cm) {
                    th.put("memo", cm.get("memo"));
                    th.put("field_type", cm.get("field_type"));
                    th.put("field_length", getInt(cm, "field_length"));
                } else {
                    th.put("memo", mapValue);
                    th.put("field_type", "string");
                    th.put("field_length", "");
                }
                retMap.put(mapKey, th);
            } else if(mapValue instanceof Collection) {//队列
                LinkedHashMap<String, Object> childFileds = new LinkedHashMap<String, Object>();
                th.put("type", "array");
                th.put("childFileds", childFileds);
                retMap.put(mapKey, th);
                rulesAutoNext(((List<Map<String, Object>>)mapValue).get(0), (null == cm || !(cm.get("childFileds") instanceof Map)) ? null : (Map<String, Map<String,Object>>)cm.get("childFileds"), childFileds);
            } else {//map
                LinkedHashMap<String, Object> childFileds = new LinkedHashMap<String, Object>();
                th.put("type", "map");
                th.put("childFileds", childFileds);
                retMap.put(mapKey, th);
                rulesAutoNext((Map<String, Object>)mapValue, (null == cm || !(cm.get("childFileds") instanceof Map)) ? null : (Map<String, Map<String,Object>>)cm.get("childFileds"), childFileds);
            }
        }
        
    }

第二步在jsp页面处理,客户可以自定义必填、字段类型、长度等等验证

 

<ul id="content">
          
</ul>
<button onClick="article_save_submit()">保存</button>

<style>
  .required {background-color :yellow;}
</style>

<script type="text/javascript">
var rules = ${rules };//上面java代码处理的map的json格式
$(function() {
  $("#content").on("change", ":checkbox", function() {//必填加个背景,黄色
    var span = $(this).closest("li").children("font");
    if($(this).is(":checked")) {
      if(!span.is(".required")) {
        span.addClass("required");
      }
    } else {
      span.removeClass("required");
    }
  })
  
  //初始化tree
  initChildDiv(rules, $("#content"));
  
  //ul下li的显示、隐藏效果
  $("#content").on("click", ".Hui-iconfont-jianhao", function() {
    $(this).parent().children("ul").hide();
    $(this).removeClass("Hui-iconfont-jianhao").addClass("Hui-iconfont-add");
  })
  //ul下li的显示、隐藏效果
  $("#content").on("click", ".Hui-iconfont-add", function() {
    $(this).parent().children("ul").show();
    $(this).removeClass("Hui-iconfont-add").addClass("Hui-iconfont-jianhao");
  })
})

//初始化ul,li
function initChildDiv(childFileds, ul) {
  var i = 0;
  for(var key in childFileds) {
    i++;
    var type = childFileds[key]['type'];
    if(type == 'field') {
      var li = $("<li style='margin-left:20px;'>"+i+".&nbsp;<font>" + key + "</font>"
            + "<span><select style='width:100px;margin-left:10px;' name='field_type'><option>string</option><option>int</option><option>float</option><option>timestamp</option></select>"
            + ",长度:<input type='text' class='ll-numberbox' style='width:100px;margin-left:10px;' data-options='min:1,max:5000' name='field_length'/>"
            + ",必填:<input type='checkbox' name='required' value='1'/>"
            + "<span style='display:none;'><input type='text' name='type' disabled readonly/></span>"
            + "<input type='text' name='name' style='display:none;'/>"
            + ",中文描述:<input type='text' name='memo' style='width:300px;'/></span></li>");
      var obj = childFileds[key];
      obj['name'] = key;
      ul.append(li);
      li.formFill(childFileds[key]);
    } else {
      var ul_next = $("<ul><ul>");
      var li = $("<li style='margin-left:20px;'><i class='Hui-iconfont Hui-iconfont-jianhao'></i>&nbsp;"+i+".&nbsp;<font>" + key + "</font>"
          + "<span><select name='type' readonly style='width:100px;margin-left:10px;background-color:grey;'><option>array</option><option>map</option><option>field</option></select>"
          + ",必填:<input type='checkbox' name='required' value='1'/>"
          + "<input type='text' name='name' style='display:none;'/>"
          + ",中文描述:<input type='text' name='memo' style='width:300px;'/></span></li>");
      var obj = childFileds[key];
      obj['name'] = key;
      li.append(ul_next);
      ul.append(li);
      li.formFill(childFileds[key]);
      initChildDiv(childFileds[key]['childFileds'], ul_next);
    }
  }
}

//保存
  function article_save_submit() {
    var obj = new Object();
    $("#content > li").each(function() {
      var span = $(this).children("span");
      var o = span.formSerialize();
      var key = o.name;
      obj[o.name] = o;
      if(o.type == 'field') {
      } else {
        var childFileds = {};
        rule_($(this).children("ul"), childFileds);
        o['childFileds'] = childFileds;
      }
    })
    console.log(obj);
    //alert(JSON.stringify(obj))
  }
  
//递归处理ul,li
  function rule_(ul, obj) {
    var lis = ul.children('li');
    $.each(lis, function(i, item) {
      var span = $(this).children("span");
      var o = span.formSerialize();
      var key = o.name;
      obj[o.name] = o;
      if(o.type == 'field') {
      } else {
        var childFileds = {};
        rule_($(this).children("ul"), childFileds);
        o['childFileds'] = childFileds;
      }
    })
  }

//自动把data中的数据根据名称填充到form(支持所有,div,span,a均可)中
//div等中元素序列化成对象
//$("form").formFill(data);
$.fn.extend({
formSerialize : function() {// 将元素中的 input,select,textarea 转成对象
 var obj = {};
 var txtSelect = $(this).find("input,select,textarea").not(":button,:reset,:submit");
 $.each(txtSelect, function(i, item) {
   var $this = $(item);
   var name = $this.attr("name");
   if(name == '' || typeof name == 'undefined') {
     return true;
   }
   var val = $this.val();
   if(($this.is(":radio") || $this.is(":checkbox")) && !$this.is(":checked")) {
     val = '';
   }
   if(obj.hasOwnProperty(name)) {
     if(val != '') {
       obj[name] =  obj[name] == '' ? val : obj[name] + "," + val;
     }
   } else {
     obj[name] = val;
   }
 })
 return obj;
},
formFill : function(data) {
 var $form = $(this);
 var txtSelect = $form.find("input, select, textarea").not(":button,:reset,:submit");
 $form.find(":radio, :checkbox").prop("checked", false);
 txtSelect.not(":radio,:checkbox").val('');
 
 var names = [];
 $.each(txtSelect, function(i, item) {
   var $this = $(item);
   var name = $this.attr("name");
   if(name == ''|| typeof name == 'undefined' || names.contains(name)) {
     return true;
   }
   names.push(name);
   
   var val = "";
   if(data.hasOwnProperty(name)) {
     val = data[name];
   }
   if(val != null){
     val = val.toString();
   } else {
     val = '';
   }
   if($this.is("select")) {
     var all = $form.find("select[name='" + name + "']");
     if(all.length == 1) {
       $this.attr("data-default", val);
       $this.val(val);
       if($this.val() == val) {
         $this.change();
       } else {
         $this.val('').change();
       }
     } else {
       val = val.split(",");
       $.each(all, function(j, jtem) {
         $(jtem).val('').change();
         $(jtem).attr("data-default", val[j]);
         if($(jtem).find("option[value='" + val[j] + "']").length == 0) {
           $(jtem).val('').change();
         } else {
           $(jtem).val(val[j]).change();
         }
       })
     }
   } else if ($this.is("textarea")) {
     var all = $form.find("textarea[name='" + name + "']");
     if(all.length == 1) {
       $this.val(val).change();
     } else {
       val = val.split(",");
       $.each(all, function(j, jtem) {
         $(jtem).val(val[j]).change();
       })
     }
   } else {
     if($this.is(":radio")) {
       if(val != '')
         $form.find(":radio[name='" + name + "'][value='" + val +  "']").prop("checked", true).change();
     } else if($this.is(":checkbox")) {
       val = val.split(",");
       $.each(val, function(m, n) {
         if(n != '')
           $form.find(":checkbox[name='" + name + "'][value='" + n +  "']").prop("checked", true).change();
       })
     } else {// :text,:email 等等
       var all = $form.find("input[name='" + name + "']");
       if(all.length == 1) {
         $this.val(val).change();
       } else {
         val = val.split(",");
         $.each(all, function(j, jtem) {
           $(jtem).val(val[j]).change();
         })
       }
     }
   }
 })
}
})
</script>

页面效果如下

项目还涉及报文的入库问题,客户要求解析并存到关系型数据库中。。。,后面会再说明

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值