I am working on a code that extracts data from a JSON file here is the JSON file: Google CDN
and here is a sample of JSON code:
{
"syncToken": "1677578581095",
"creationTime": "2023-02-28T02:03:01.095938",
"prefixes": [{
"ipv4Prefix": "34.80.0.0/15",
"service": "Google Cloud",
"scope": "asia-east1"
}, {
"ipv4Prefix": "34.137.0.0/16",
"service": "Google Cloud",
"scope": "asia-east1"
}, {
"ipv4Prefix": "35.185.128.0/19",
"service": "Google Cloud",
"scope": "asia-east1"
}, {
"ipv6Prefix": "2600:1900:40a0::/44",
"service": "Google Cloud",
"scope": "asia-south1"
},
I know where the problem is but can not fix the issue with solutions on this website and getting another error every time.
This is my code
import json
f = open('cloud.json')
data = json.load(f)
array = []
for i in data['prefixes']:
array = [i['prefix'] for i in data['ipv4Prefix']]
f_path = (r"ip.txt")
with open (f_path ,'w') as d:
for lang in array:
d.write("{}n".format(lang))
f.close()
Basically I want to extract only ipv4 address but there are some ipv6 address randomly in block that causes this error so I get key error like this: KeyError: ‘ipv4Prefix’
I know why I am getting this error so I tried deleting that whole entry with ipv6Prefix so I added this part to my code:
if data[i]["prefixes"] == "ipv6Prefix":
data.pop(i)
for this one I get TypeError: unhashable type: ‘dict’ which is new to me, I also tried this as someone pointed out in another question but it didn’t work.
del data[ipv6Prefix]
Now my final code is like this and getting this error: TypeError: list indices must be integers or slices, not str which is understandable.
import json
f = open('cloud.json')
data = json.load(f)
array = []
for i in data['prefixes']:
if [i]["prefixes"] == ['ipv6Prefix']:
data.pop(i)
array = [i['prefix'] for i in data['ipv4Prefix']]
f_path = (r"ip.txt")
with open (f_path ,'w') as d:
for lang in array:
d.write("{}n".format(lang))
f.close()
So how can I delete entries with ‘ipv6Prefix’ or better to say, ignore them in my for loop?
I found this question but answer does not fit my code at all.
what’s the problem with my code?
I tried several methods like del
and dict.pop()
but still I get error.
2
Answers
You have two choices: Look Before You Leap or Easier to Ask Forgiveness than Permission. In short:
if
check to make sureipv4Prefix
existsipv4Prefix
exists but catch the exception (aKeyError
in this case)Here is some code that demonstrates both approaches. It does not include writing out the results.
Which style to use is subjective. Python has a reputation for preferring EAFP, but I prefer to use LYBL if errors are expected as part of normal operation. In your case you know that some objects will not have
ipv4Prefix
, so I contend that LBYL is more suitable here.You can skip/ignore prefixes containing
ipv6Prefix
withif...continue
:You can write only prefixes containing
ipv4Prefix
withif 'ipv4Prefix' in...
You can alter
data
itself to omit prefixes containingipv6Prefix
with list comprehension:You can save a list of prefixes containing
ipv4Prefix
as JSON withjson.dump
:That’s probably due to the
if [i]["prefixes"] == ['ipv6Prefix']:
line;[i]
is a list with just a single item [i
, which is a dictionary], so[i]["prefixes"]
just doesn’t make any sense. You can useif 'ipv6Prefix' in i["prefixes"]
instead, but there are more issues with what you’re trying to accomplish in that block [I’ll explain in the next section].The
.pop
method only takes an integer as input [which has to be the index of the item you want to remove from that list], buti
is a copy of a dictionary insidedata['prefixes']
, so.pop(i)
would raise an error if there’s an attempt to execute it.You could loop through
enumerate
(data['prefixes'])
(instead of justdata['prefixes']
) to keep track of the index associatedi
, but keep in mind that looping through a list topop
multiple items [from that same list] is NOT advisable at all. For example, if you pop the second item from the list [index=1], then the indices of all items after it will decrease by one; so if you next need to pop what was originally the 5th item in the list,enumerate
will tell you that its index is 4, but it actually became 3 after executing.pop(1)
…You could loop through the list in reverse as below (but isn’t the list-comprehension approach I suggested before simpler?)
Btw, instead of applying
reversed
, you can also use slicing likedata['prefixes'][::-1]
. I just thought using the function is better for readability because it makes it very obvious what it’s doing.i
is a dictionary (which is unhashable, as the error message said), and therefore cannot be used as a key the way....data[i]...
is trying to.probably from the
data['ipv4Prefix']
bit in thearray = [i['prefix'] for i in data['ipv4Prefix']]
, becausedata
does not have a keyipv4Prefix
; somei
s infor i in data['prefixes']
might, but there is no point in usingif 'ipv4Prefix' in i: del i
becausei
is a copy of an item in the list being looped though.You can try using
.remove
likedata['prefixes'].remove(i)
[instead ofdel i
], but I don’t think that would be very efficient. List comprehension is definitely my preferred method in this case [and also probably considered the most "pythonic" approach here].