首页 > 解决方案 > 如何在 Groovy 中展平和拆分这个 JSON?

问题描述

我可以使用一些帮助来编写 Groovy 脚本,以基于嵌套数组元素将 JSON 展平和拆分为多个 JSON-s。这是原始的 JSON:

{
  "input_query": {
    "discount_guid": "3afeb169-7969-4f6f-8928-d801692848b1",
    "user_uid": 5467890,
    "shopping_list": [
      {
        "article_id": 311729,
        "current_price_without_promo": 7.69,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 229752,
        "current_price_without_promo": 11.29,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 193672,
        "current_price_without_promo": 79.99,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 261657,
        "current_price_without_promo": 16.99,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 318153,
        "current_price_without_promo": 13.99,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      }
    ],
    "discount_params_per_article": [
      {
        "article_id": 311729,
        "min_discount": 0,
        "max_discount": 4.12,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 229752,
        "min_discount": 0,
        "max_discount": 7.52,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 193672,
        "min_discount": 0,
        "max_discount": 60,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 261657,
        "min_discount": 0,
        "max_discount": 12.4,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 318153,
        "min_discount": 0,
        "max_discount": 8,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      }
    ],
    "target_probability_increase": null,
    "request_time": "2019-12-21T21:32:13.018635"
  },
  "total_discount": 0.94,
  "article_discounts": [
    {
      "article_id": 311729,
      "discount": 0.04
    },
    {
      "article_id": 229752,
      "discount": 0.08
    },
    {
      "article_id": 193672,
      "discount": 0.61
    },
    {
      "article_id": 261657,
      "discount": 0.13
    },
    {
      "article_id": 318153,
      "discount": 0.08
    }
  ]
}

我想做的是将原始 JSON 展平为 JSON-s 数组,如下所示:

[{
  "discount_guid": "3afeb169-7969-4f6f-8928-d801692848b1",
  "user_uid": 5467890,
  "article_id": 318153,
  "current_price_without_promo": 13.99,
  "promo_discount": 0,
  "count": 1,
  "apply_discount": true,
  "min_discount": 0,
  "max_discount": 8,
  "imposed_discount": null,
  "article_target_probability_increase": 1.15,
  "discount_downscale_factor": 1,
  "target_probability_increase": null,
  "request_time": "2019-12-21T21:32:13.018635",
  "total_discount": 0.94,
  "discount": 0.08
},
{
  "discount_guid": ...
},
...
]

我已经设法通过这种方式获得了一个扁平的 JSON:

import groovy.json.JsonOutput as jo

def content = new File('response.json')
def slurper = new groovy.json.JsonSlurper()
def object = slurper.parseText(content)

def flattenMap(Map map) {
    def result = [:]

    map.each { k, v ->
        if (v instanceof Map) {
            result << flattenMap(v)
        } else if (v instanceof Collection && v.every {it instanceof Map}) {
            v.each {
                result << flattenMap(it)
            }
        } else {
            result[k] = v
        }
    }
    result
}

println(jo.prettyPrint(jo.toJson(flattenMap(object))))

但我不知道如何获得完整的 JSON-s 数组。我确信有一种简单的方法可以实现这一点,但我对 Groovy 还是很陌生,到目前为止我还没有找到解决方案。任何帮助将不胜感激。

标签: jsongroovynestedflatten

解决方案


解决这个问题的一种方法是这样的:

import groovy.json.*

def str = '''
{
  "input_query": {
    "discount_guid": "3afeb169-7969-4f6f-8928-d801692848b1",
    "user_uid": 5467890,
    "shopping_list": [
      {
        "article_id": 311729,
        "current_price_without_promo": 7.69,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 229752,
        "current_price_without_promo": 11.29,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 193672,
        "current_price_without_promo": 79.99,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 261657,
        "current_price_without_promo": 16.99,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      },
      {
        "article_id": 318153,
        "current_price_without_promo": 13.99,
        "promo_discount": 0,
        "count": 1,
        "apply_discount": true
      }
    ],
    "discount_params_per_article": [
      {
        "article_id": 311729,
        "min_discount": 0,
        "max_discount": 4.12,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 229752,
        "min_discount": 0,
        "max_discount": 7.52,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 193672,
        "min_discount": 0,
        "max_discount": 60,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 261657,
        "min_discount": 0,
        "max_discount": 12.4,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      },
      {
        "article_id": 318153,
        "min_discount": 0,
        "max_discount": 8,
        "imposed_discount": null,
        "article_target_probability_increase": 1.15,
        "discount_downscale_factor": 1
      }
    ],
    "target_probability_increase": null,
    "request_time": "2019-12-21T21:32:13.018635"
  },
  "total_discount": 0.94,
  "article_discounts": [
    {
      "article_id": 311729,
      "discount": 0.04
    },
    {
      "article_id": 229752,
      "discount": 0.08
    },
    {
      "article_id": 193672,
      "discount": 0.61
    },
    {
      "article_id": 261657,
      "discount": 0.13
    },
    {
      "article_id": 318153,
      "discount": 0.08
    }
  ]
}
'''

def json = new JsonSlurper().parseText(str) 

// a predicate to check if a value is a plain value vs map or list
def isPlain     = { v -> !(v instanceof Map) && !(v instanceof List) }

// for the two maps json.input_query and the root level json map, 
// find all plain values
def plainValues = json.input_query.findAll { k, v -> isPlain(v) } +
                  json.findAll { k, v -> isPlain(v) } 

// find the three lists of maps, group by article_id and add the 
// values for each article id to a cumulative map and finally 
// add the plain values collected above to each cumulative map
def result = (json.input_query.shopping_list +
              json.input_query.discount_params_per_article +
              json.article_discounts).groupBy { 
                it.article_id 
              }.values().collect { listOfMaps -> 
                listOfMaps.sum() + plainValues
              }

// print result
result.each { m -> 
  println "-----"
  m.sort().each { k, v -> 
    println "${k.padLeft(35)} -> $v"
  }
}

执行上述产生:

─➤ groovy solution.groovy
-----
                     apply_discount -> true
                         article_id -> 311729
article_target_probability_increase -> 1.15
                              count -> 1
        current_price_without_promo -> 7.69
                           discount -> 0.04
          discount_downscale_factor -> 1
                      discount_guid -> 3afeb169-7969-4f6f-8928-d801692848b1
                   imposed_discount -> null
                       max_discount -> 4.12
                       min_discount -> 0
                     promo_discount -> 0
                       request_time -> 2019-12-21T21:32:13.018635
        target_probability_increase -> null
                     total_discount -> 0.94
                           user_uid -> 5467890
-----
                     apply_discount -> true
                         article_id -> 229752
article_target_probability_increase -> 1.15
                              count -> 1
        current_price_without_promo -> 11.29
                           discount -> 0.08
          discount_downscale_factor -> 1
                      discount_guid -> 3afeb169-7969-4f6f-8928-d801692848b1
                   imposed_discount -> null
                       max_discount -> 7.52
                       min_discount -> 0
                     promo_discount -> 0
                       request_time -> 2019-12-21T21:32:13.018635
        target_probability_increase -> null
                     total_discount -> 0.94
                           user_uid -> 5467890
-----
...

最后的打印输出按键排序,并为可读性做了一些缩进。

然后,您可以使用以下方式获取输出 json:

def outputJson = JsonOutput.toJson(result)

推荐阅读