takotsubo
takotsubo

Reputation: 746

Modify JSON in Groovy

I have a JSON:

{
  "ad_id" : "795220717",
  "date_start" : "2021-03-02",
  "account_currency" : "EUR",
  "account_name" : "Bonduelle",
  "actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  }, {
    "action_type" : "post_engagement",
    "value" : "1"
  } ],
  "buying_type" : "AUCTION",
  "clicks" : "241",
  "cost_per_action_type" : [ {
    "action_type" : "video_view",
    "value" : "0.010006"
  }, {
    "action_type" : "page_engagement",
    "value" : "0.010006"
  }, {
    "action_type" : "post_engagement",
    "value" : "0.010006"
  } ],
  "cost_per_unique_action_type" : [ {
    "action_type" : "video_view",
    "value" : "0.010006"
  }, {
    "action_type" : "page_engagement",
    "value" : "0.010006"
  }, {
    "action_type" : "post_engagement",
    "value" : "0.010006"
  } ],
  "cpm" : "10.005512",
  "ctr" : "0",
  "spend" : "0.010006",
  "unique_actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  }, {
    "action_type" : "page_engagement",
    "value" : "1"
  }, {
    "action_type" : "post_engagement",
    "value" : "1"
  } ],
  "unique_clicks" : "13",
  "video_avg_time_watched_actions" : [ {
    "action_type" : "video_view",
    "value" : "5"
  } ],
  "video_p25_watched_actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  } ],
  "video_play_actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  } ],
  "created_time" : "2021-02-19",
  "impression_device" : "other",
  "publisher_platform" : "instagram",
  "platform_position" : "feed"
}

Expected JSON after transformations:

[ {
    "ad_id" : "795220717",
    "impression_device" : "other",
    "publisher_platform" : "instagram",
    "action_type" : "video_view",
    "value" : "1",
    "field_name": "actions"
  }, {
    "ad_id" : "795220717",
    "impression_device" : "other",
    "publisher_platform" : "instagram",
    "action_type" : "post_engagament",
    "value" : "1",
    "field_name": "actions"
  }, {
    "ad_id" : "795220717",
    "impression_device" : "other",
    "publisher_platform" : "instagram",
    "action_type" : "post_engagament",
    "value" : "0.010006",
    "field_name": "cost_per_action_type"
   }, 
 ... etc
]

What should I do:

  1. Build arrays based on action_type. So if array in source JSON contains action_type I should loop through this array and perform steps below.
  2. Add ad_id, impression_device, publisher_platform for each action_type - they will be the same for all arrays inside expected JSON.
  3. Set field_name: it is a JSON array name for action_type: For example:
 "actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  }, {
    "action_type" : "post_engagement",
    "value" : "1"
  } ]

field_name should be actions.

  1. And there is the problem. Some JSONs may contain actions, unique_actions but don't contain cost_per_action_type. So I cant loop through based on these names and I should loop through all JSON keys and find and find those that are an array and contain action_type inside.

I don't know where begin. Tried to start with:

def slurped = new JsonSlurper().parseText(content)
def map = slurped.findAll { 
   it.key instanceof List
}

But it returns empty map. First question I need to solve: how to filter arrays inside JSON which contains action_type?

UPDATE

JSON with multiple elements inside:

[
   {
      "ad_id":"795220717",
      "date_start":"2021-03-02",
      "actions":[
         {
            "action_type":"video_view",
            "value":"1"
         },
         {
            "action_type":"post_engagement",
            "value":"1"
         }
      ],
      "buying_type":"AUCTION",
      "clicks":"241",
      "cost_per_action_type":[
         {
            "action_type":"video_view",
            "value":"0.010006"
         },
         {
            "action_type":"page_engagement",
            "value":"0.010006"
         },
         {
            "action_type":"post_engagement",
            "value":"0.010006"
         }
      ],
      "cost_per_unique_action_type":[
         {
            "action_type":"video_view",
            "value":"0.010006"
         },
         {
            "action_type":"page_engagement",
            "value":"0.010006"
         },
         {
            "action_type":"post_engagement",
            "value":"0.010006"
         }
      ],
      "cpm":"10.005512",
      "ctr":"0",
      "spend":"0.010006",
      "unique_clicks":"13",
      "created_time":"2021-02-19",
      "impression_device":"other",
      "publisher_platform":"instagram",
      "platform_position":"feed"
   },
   {
      "ad_id":"220717",
      "date_start":"2021-03-02",
      "account_currency":"USD",
      "account_name":"Nestle",
      "actions":[
         {
            "action_type":"video_view",
            "value":"1"
         },
         {
            "action_type":"post_engagement",
            "value":"1"
         }
      ],
      "buying_type":"AUCTION",
      "clicks":"241",
      "cpm":"10.005512",
      "ctr":"0",
      "spend":"0.010006",
      "video_avg_time_watched_actions":[
         {
            "action_type":"video_view",
            "value":"5"
         }
      ],
      "video_p25_watched_actions":[
         {
            "action_type":"video_view",
            "value":"1"
         }
      ],
      "video_play_actions":[
         {
            "action_type":"video_view",
            "value":"1"
         }
      ],
      "created_time":"2021-02-19",
      "impression_device":"other",
      "publisher_platform":"instagram",
      "platform_position":"feed"
   }
]

Upvotes: 0

Views: 203

Answers (2)

injecteer
injecteer

Reputation: 20699

Something straight-forward:

import groovy.json.*

String s = '''
{
  "ad_id" : "795220717",
  "date_start" : "2021-03-02",
  "account_currency" : "EUR",
  "account_name" : "Bonduelle",
  "actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  }, {
    "action_type" : "post_engagement",
    "value" : "1"
  } ],
  "buying_type" : "AUCTION",
  "clicks" : "241",
  "cost_per_action_type" : [ {
    "action_type" : "video_view",
    "value" : "0.010006"
  }, {
    "action_type" : "page_engagement",
    "value" : "0.010006"
  }, {
    "action_type" : "post_engagement",
    "value" : "0.010006"
  } ],
  "cost_per_unique_action_type" : [ {
    "action_type" : "video_view",
    "value" : "0.010006"
  }, {
    "action_type" : "page_engagement",
    "value" : "0.010006"
  }, {
    "action_type" : "post_engagement",
    "value" : "0.010006"
  } ],
  "cpm" : "10.005512",
  "ctr" : "0",
  "spend" : "0.010006",
  "unique_actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  }, {
    "action_type" : "page_engagement",
    "value" : "1"
  }, {
    "action_type" : "post_engagement",
    "value" : "1"
  } ],
  "unique_clicks" : "13",
  "video_avg_time_watched_actions" : [ {
    "action_type" : "video_view",
    "value" : "5"
  } ],
  "video_p25_watched_actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  } ],
  "video_play_actions" : [ {
    "action_type" : "video_view",
    "value" : "1"
  } ],
  "created_time" : "2021-02-19",
  "impression_device" : "other",
  "publisher_platform" : "instagram",
  "platform_position" : "feed"
}'''

Map json = new JsonSlurper().parseText s

Map common = json.findAll{ k, _ -> k in [ 'ad_id', 'impression_device', 'publisher_platform' ] }

List result = json.inject( [] ){ res, k, v ->
  if( ( v in List ) && v.any{ it.action_type } ) v.each{ res << common + [ field_name:k ] + it }
  res
}

JsonOutput.prettyPrint JsonOutput.toJson( result )

prints:

[
    {
        "ad_id": "795220717",
        "impression_device": "other",
        "publisher_platform": "instagram",
        "field_name": "actions",
        "action_type": "video_view",
        "value": "1"
    },
    {
        "ad_id": "795220717",
        "impression_device": "other",
        "publisher_platform": "instagram",
        "field_name": "actions",
        "action_type": "post_engagement",
        "value": "1"
    },
    {
        "ad_id": "795220717",
        "impression_device": "other",
        "publisher_platform": "instagram",
        "field_name": "cost_per_action_type",
        "action_type": "video_view",
        "value": "0.010006"
    },
    etc...
]

UPDATE:

In case of List of Maps the code changes a bit:

import groovy.json.*

String s = '''
[ {
  "ad_id" : "795220717",
   etc....
} ]'''

List json = new JsonSlurper().parseText s

List result = json.inject( [] ){ res, elem ->
  Map common = elem.findAll{ k, _ -> k in [ 'ad_id', 'impression_device', 'publisher_platform' ] }
  elem.each{ k, v ->
    if( ( v in List ) && v.any{ it.action_type } ) v.each{ res << common + [ field_name:k ] + it }
  }
  res
}

The output remains the same

UPDATE 2:

  Map common = [ 'ad_id', 'impression_device', 'publisher_platform' ].collectEntries{ [ it, elem[ it ] ?: '' ] }

Upvotes: 3

tim_yates
tim_yates

Reputation: 171084

Another alternative would be:

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

def entries = { data, field ->
    data."$field".collect {
        [
            'ad_id': data.ad_id,
            'impression_device': data.impression_device,
            'publisher_platform': data.publisher_platform,
            'action_type': it.action_type,
            'value': it.value,
            'field_name': field
        ]
    }
}

def result = entries(model, 'actions') + entries(model, 'cost_per_action_type')
JsonOutput.prettyPrint JsonOutput.toJson(result)

Upvotes: 1

Related Questions