Search code examples
pythonjsonapiriot-games-api

JSON from (RIOT) API Formatted Incorrectly


I am importing JSON data into Python from an API and ran into the following decode error:

JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

Looking at online examples its immediately clear my JSON data has ' where others have have ".

Ideally, I'd like to know why it's being downloaded in this way. It seems highly likely its an error on my end, not theirs.

I decided it should be easy to correct the JSON format but I have failed here too. Please see the below code for how I obtain the JSON data and my attempt at fixing it.

#----------------------------------------
#---Read this only if you want to download 
#---the data yourself. 
#----------------------------------------

#Built from 'Towards Data Science' guide
#https://towardsdatascience.com/how-to-use-riot-api-with-python-b93be82dbbd6

#Must first have installed riotwatcher

#Info in my example is made up, I can't supply a real API code or
#I get in trouble. Sorry about this. You could obtain one from their website
#but this would be a lot of faff for what is probably a simple StackOverflow
#question

#If you were to get/have a key you could use the following information:
#<EUW> for region
#<Agurin> for name

#----------------------------------------
#---Code
#----------------------------------------


#--->Set Variables


#Get installed riotwatcher module for
#Python
import riotwatcher

#Import riotwatcher tools.
from riotwatcher import LolWatcher, ApiError

#Import JSON (to read the JSON API file)
import json

# Global variables
# Get new API from
# https://developer.riotgames.com/
api_key = 'RGAPI-XXXXXXX-XXX-XXXX-XXXX-XXXX'
watcher = LolWatcher(api_key)
my_region = 'MiddleEarth'

#need to give path to where records
#are to be stored
records_dir = "/home/solebaysharp/Projects/Riot API/Records"


#--->Obtain initial data, setup new varaibles

#Use 'watcher' to get basic stats and setup my account as a variable (me)
me = watcher.summoner.by_name(my_region, "SolebaySharp")

# Setup retrieval of recent match info
my_matches = watcher.match.matchlist_by_account(my_region, me["accountId"])
print(my_matches)

#--->Download the recent match data

#Define where the JSON data is going to go
recent_matches_index_json = (records_dir + "/recent_matches_index.json")

#get that JSON data
print ("Downloading recent match history data")
file_handle = open(recent_matches_index_json,"w+")
file_handle.write(str(my_matches))
file_handle.close()

#convert it to python
file_handle = open(recent_matches_index_json,)
recent_matches_index = json.load(file_handle)

Except this giver the following error...

JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

So instead to correct this I tried:

file_handle = open(recent_matches_index_json)

json_sanitised = json.loads(file_handle.replace("'", '"'))

This returns...

AttributeError: '_io.TextIOWrapper' object has no attribute 'replace'

For the sake of completeness, beneath is a sample of what the JSON looks like. I have added the paragraphs to enhance readability. It does not come this way.

{'matches': [
  {'platformId': 'NA1',
   'gameId': 5687555181,
   'champion': 235,
   'queue': 400,
   'season': 13,
   'timestamp': 1598243995076,
   'role': 'DUO_SUPPORT',
   'lane': 'BOTTOM'
   },
   {'platformId': 'NA1',
   'gameId': 4965733458,
   'champion': 235,
   'queue': 400,
   'season': 13,
   'timestamp': 1598240780841,
   'role': 'DUO_SUPPORT',
   'lane': 'BOTTOM'
   },
   {'platformId': 'NA1',
   'gameId': 4583215645,
   'champion': 111,
   'queue': 400,
   'season': 13,
   'timestamp': 1598236666162,
   'role': 'DUO_SUPPORT',
   'lane': 'BOTTOM'
   }],
'startIndex': 0,
'endIndex': 100,
'totalGames': 186}

Solution

  • This is occurring because Python is converting the JSON to a string (str).

    file_handle.write(str(my_matches))
    

    As it doesn't see the difference between ' and " it just goes with its default of '.

    We can stop this from happening by using JSON.dumps. This partially (certainly not fully) answers the second part of the question as well, as we're using a correctly formatted JSON command.

    The above-mentioned line simply has to be replaced with this:

    file_handle.write(json.dumps(my_matches))
    

    This will preserve the JSON formatting.