JSON解析拍平工具

JSON解析拍平工具

背景:
问题:在做数据清洗的时候,拿到的数据是爬虫爬取的数据,存在数据结构不一致且同
时包含JSON对象和JSON数组,以及JSON嵌套,数据结构复杂,没法统一处理,所以
希望有个工具能把获取到的数据做个自动的拍平和数组的拆分,给后面做数据转换和清
洗准备好数据

解决:整体思路是走递归处理,在拿到json的时候,需要判断每个字段的内容类型
(数组、对象、字段),对应不同的操作。在操作过程中需要把父节点的json串信息
往下传,因为会涉及到json数组拆分,这样一条json会被拆分成
(数组大小的倍数N*当前条M)数条,如果有数组嵌套,一直往下乘。拆分好json对往list里面输出,判断输出的依据就是,处理到当前json的字段都没有json对象和json数组的时候,说明json已经到底了

代码:

class JsonFlattener1 (val node: AnyRef) {

  val father_json=new JSONObject()

  var json_list=new util.ArrayList[JSONObject]
  val column_set=new util.HashSet[String]()

//  val column_set:mutable.HashSet[String]=new mutable.HashSet()

  def flatten1(): util.ArrayList[JSONObject] = {
    process(node, father_json,"",column_set)
    return json_list
  }
  
  def process(node: AnyRef, common: JSONObject,prefix:String,common_column:util.HashSet[String]){
    node match {
      case x: JSONObject => {
//        var obj_list=new util.ArrayList[JSONObject]
        var obj_map=new mutable.HashMap[String,JSONObject]()
        var obj_common=common//这里不做深拷贝,这样在多json循环处理的时候父json会一直变
        var self_set=new util.HashSet[String]()
        common_column.forEach(aa=>{
          self_set.add(aa)
        })

        var obj_count=0
        var count = 0//用count来标记当前json的各个字段是否包含json对象或者json数组,如果处理完count还是0那么说明这个json已经处理完了,后面不会有嵌套的json,所以当作输出的标志

        //在这里处理解决的是,在提前知道下一级目录的情况下,把字段装进json,这里按顺序处理字段,json对象,json数组
        x.entrySet().asScala.foreach(ele => {
          val (k, v) = (ele.getKey, ele.getValue)
          if (!v.isInstanceOf[JSONObject] && !v.isInstanceOf[JSONArray]) //字段不包含json
          {
            //  处理同名的列
            if (self_set.contains(k)){
              obj_common.put((prefix+"/"+k), v)
              self_set.add(prefix+"/"+k)
            }
            else{
              obj_common.put(k, v)
              self_set.add(k)
            }

            //这里处理数组里面是字符串或者数字的(非json)
          }else if(ele.getValue.isInstanceOf[JSONArray] && ele.getValue.asInstanceOf[JSONArray].size()>0){
            if(!ele.getValue.asInstanceOf[JSONArray].get(0).isInstanceOf[JSONObject]){
              //  处理同名的列
              if (self_set.contains(k) ){
                obj_common.put((prefix+"/"+k), v)
                self_set.add(prefix+"/"+k)
              }
              else{
                obj_common.put(k, v)
                self_set.add(k)
              }
            }
          }
        })


        //字段包含json对象
        x.entrySet().asScala.foreach(ele => {

          val (k, v) = (ele.getKey, ele.getValue)
          if (v.isInstanceOf[JSONObject])
          {
            obj_count+=1
            count += 1
            obj_map.put(k,parseCopyObj(v.asInstanceOf[JSONObject]))
//            obj_list.add(parseCopyObj(v.asInstanceOf[JSONObject]))
          }
        })
//处理同一级json多个字段都是json的情况,
        if (obj_count>0){
          process(extendObj(obj_map,prefix), obj_common,prefix,self_set)
        }


        x.entrySet().asScala.foreach(ele => {
          val (k, v) = (ele.getKey, ele.getValue)
          if (v.isInstanceOf[JSONArray]  &&  v.asInstanceOf[JSONArray].size()>0 ) //字段包含json数组
          {
            if (v.asInstanceOf[JSONArray].get(0).isInstanceOf[JSONObject]){
              count += 1
              process(v, obj_common,prefix+"/"+k,self_set)
            }
          }
        })

        if (count == 0) {
          json_list.add(parseCopyObj(obj_common))
        }

      }

      case x: JSONArray => {
        if(x.size()>0  ){//只处理非空的json数组
          var arr_map=new mutable.HashMap[String,JSONObject]()
//          var arr_list=new util.ArrayList[JSONObject]
          var ar_count = 0
          var obj_count = 0
          for (i <- 0 until x.size()) {
            println(i+"——————————"+common_column)
            process(x.get(i),common, prefix,common_column)
          }
        }else{
          json_list.add(parseCopyObj(common))
        }
      }
    }


  }
  //json深拷贝,在json传递的时候如果不做深拷贝,那么赋值的是变量地址
  def parseCopyObj(obj:JSONObject):JSONObject={
    val outObj=new JSONObject()
    obj.entrySet().asScala.foreach(ee=>{
      outObj.put(ee.getKey,ee.getValue)
    })
    outObj
  }


  //obj2复制到obj1
  //这里的prefix不能做返回,因为json是多个json拼接的,所以prefix是不一样的,不能做统一返回,只能用上一层级的prefix,这会导致一个问题
  //在多层json对象嵌套的时候,当前层的json只能获取到父节点的字段名,获取不到爷爷节点的字段名,这里需要注意
  def extendObj(obj_map:mutable.HashMap[String,JSONObject],prefix:String):JSONObject={
    var self_set=new util.HashSet[String]()
    column_set.forEach(aa=>{
      self_set.add(aa)
    })
    var trace=""
    if(prefix.isEmpty)trace else trace=prefix+"/"

    val outObj=new JSONObject()
    obj_map.iterator.foreach(el=>{
      el._2.entrySet().asScala.foreach(ee=>{
        if (self_set.contains(ee.getKey)){
          outObj.put((trace+el._1+"/"+ee.getKey),ee.getValue)
          self_set.add(trace+el._1+"/"+ee.getKey)
        }else{
          outObj.put(ee.getKey,ee.getValue)
          self_set.add(ee.getKey)
        }
      })
    })
    outObj
  }

}

难点:

1、判断何时递归停止做数据输出:
这个逻辑需要考虑在处理字段的时候,其他字段的值可能含有json对象或者json数组,所以对一个json的处理的要遍历三次字段,判断字段包含的值(顺序分别是正常值、json对象、json数组),保证处理完当前json的字段后再处理嵌套的json。

2、处理同名字段:
在json嵌套的过程中,会出现同名字段,这这时候需要把重名的字段全路径标出来》

总结

代码可以直接用,参数传的是json

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值