使用 Groovy 动态解析 JSON

Dynamically Parsing JSON with Groovy

我从支持系统 API 撤回了一份 JSON 文档。使用我的代码,我想动态地提取预配置的字段,假设当我的程序调用 API.

时 JSON 可能有更多或更少的所需字段

我有一些有效的代码,尽管它看起来非常复杂且效率低下。

以下是我感兴趣的 JSON 片段:

{
   "rows": [
      {
         "assignee_id": 1,
         "created": "2017-01-25T14:13:19Z",
         "custom_fields": [],
         "fields": [],
         "group_id": 2468,
         "priority": "Low",
         "requester_id": 2,
         "status": "Open",
         "subject": "Support request",
         "ticket": {
            "description": "Ticket descritpion",
            "id": 1000,
            "last_comment": {
               "author_id": 2,
               "body": "Arbitrary text",
               "created_at": "2017-02-09T14:21:38Z",
               "public": false
            },
            "priority": "low",
            "status": "open",
            "subject": "Support request",
            "type": "incident",
            "url": "Arbitrary URL"
         },
         "updated": "2017-02-09T14:21:38Z",
         "updated_by_type": "Agent"
      },
      {
         "assignee_id": 1,
         "created": "2017-02-09T14:00:18Z",
         "custom_fields": [],
         "fields": [],
         "group_id": 3579,
         "priority": "Normal",
         "requester_id": 15,
         "status": "Open",
         "subject": "Change request",
         "ticket": {
            "description": "I want to change this...",
            "id": 1001,
            "last_comment": {
               "author_id": 20,
               "body": "I want to change the CSS on my website",
               "created_at": "2017-02-09T14:12:12Z",
               "public": true
            },
            "priority": "normal",
            "status": "open",
            "subject": "Change request",
            "type": "incident",
            "url": "Arbitrary URL"
         },
         "updated": "2017-02-09T14:12:12Z",
         "updated_by_type": "Agent"
      }
   ]
}

我有一个名为 wantedFields 的 ArrayList,我从配置中构建它来定义我想从 JSON:

中提取哪些信息
  ["id","subject","requester_id","status","priority","updated","url"]

复杂性在于数据在 API 中被复制,我只想提取一次数据,在适用的情况下优先选择 "rows" 中的数据。我这样做的方法如下。感觉就像我在重复代码,但我真的看不出如何使这项工作更有效。 JSON 保存为 "viewAsJson"。

def ArrayList<Map<String,Object>> assignConfiguredFields(viewAsJson, wantedFields) {
    //Pull out configured fields from JSON and store as Map to write as CSV later
    ArrayList<Map<String,Object>> listOfDataToWrite = new ArrayList<Map<String,Object>>()

    ArrayList<String> rowKeyList = new ArrayList<String>()
    def validationRow = viewAsJson.rows.get(0)
    //Compare one row object to config first
    validationRow.each { k, v ->
        if (wantedFields.contains(k)) {
            wantedFields.remove(k)
            rowKeyList.add(k)
        }
    }

    ArrayList<String> ticketKeyList = new ArrayList<String>()
    def validationTicket = viewAsJson.rows.ticket.get(0)

    //Compare one ticket object to config first
    validationTicket.each { k, v ->
        if (wantedFields.contains(k)) {
            wantedFields.remove(k)
            ticketKeyList.add(k)
        }
    }

    def rows = viewAsJson.rows
    def tickets = viewAsJson.rows.ticket

    //Pull matching ticket objects from JSON and store in Map

    ArrayList<Map<String,Object>> tickList= new ArrayList<>()
    ArrayList<Map<String,Object>> rowList= new ArrayList<>()
    rows.each { row ->
        Map<String,Object> rowMap = new HashMap<>()
        row.each { k, v ->
            if(rowKeyList.contains(k))
                rowMap.put(k,v)
        }
        rowList.add(rowMap)
    }

    tickets.each { ticket ->
        Map<String,Object> ticketMap = new HashMap<>()
        ticket.each { k, v ->
            if(ticketKeyList.contains(k))
                ticketMap.put(k, v)
        }
        tickList.add(ticketMap)
    }

    for (int i = 0; i < rowList.size(); i++) {
        HashMap<String,Object> dataMap = new HashMap<>()
        dataMap.putAll(rowList.get(i))
        dataMap.putAll(tickList.get(i))
        listOfDataToWrite.add(dataMap)
    }

    println listOfDataToWrite
    return listOfDataToWrite
}

我知道应该对 wantedFields ArrayList 是否仍然存在进行一些验证。我已经重复这段代码很多次了,只是这次忘了重新添加。

我不知道你是否还需要这段代码,但为什么不试试这样的东西。 有一个翻译图和运行每一行。

Object tranverseMapForValue(Map source, String keysToTranverse, Integer location = 0){
    List keysToTranverseList = keysToTranverse.split(/\./)
    tranverseMapForValue(source, keysToTranverseList, location)
}
Object tranverseMapForValue(Map source, List keysToTranverse, Integer location = 0){
    if(source.isEmpty() || keysToTranverse.isEmpty()){
        return null
    }

    String key = keysToTranverse[location]
    if(source[key] instanceof Map){
        return tranverseMapForValue(source[key], keysToTranverse, location + 1)
    }
    else{
        return source[key]
    }
}


Map translation = [
    "ticket.id": "id",
    "ticket.subject": "subject",
    "requester_id": "requester_id",
    "ticket.status": "status",
    "priority": "priority",
    "updated": "updated",
    "ticket.url": "url"
]

List rows = []

json.rows.each{ row ->
    Map mapForRow = [:]

    translation.each{ sourceKey, newKey ->
        mapForRow << [(newKey): tranverseMapForValue(row, sourceKey)]
    }

    rows.add(mapForRow)
}