2

I'm facing a problem here, because I must receive a json which contains a string with the path of some data in another json.

  1. the Json which contains some data

    json1 = {
        "Items": [{
                      "building": "buildingA",
                      "y": [1, 2, 3],
                      "yl": "A,B,C",
                      "xl": "Data",
                      "x": [1, 2, 3]
                  },
                  {
                      "y": [4, 5, 6],
                      "x": [1, 2, 3],
                      "predio": "BuildingB",
                      "yl": "D,E,F",
                      "xl": "Data"
                  }]
    }
    
  2. and the json that contains the path of the desired value:

    json2 = {
        "y": "y//1",
    }
    

I tried to make this code to solve the problem:

def size2(a,i):
 x=a.split('//')
 y=len(x)
 if y ==1:
    return i[x[0]]
 elif y==2:
    return i[x[0]][x[1]]
 elif y==3:
    return i[x[0]][x[1]][x[2]]

y=json2['y']
for i in json1['Items']:
 print(i['y'][1])        #this way works
 print(size2(y,i))       #this way fails

The error message is:

TypeERROR: list indices must be integers, not str

Does anyone know how to solve this problem?

2
  • Yes. Use integer indicies instead of strings. Commented May 30, 2017 at 0:00
  • 1
    Non-sarcastic actual help: when you do the list access it returns the member in the JSON data which is a string, which you are then trying to use for a subsequent index access, which is throwing the error. Cast the result to an int: int(x[1]). Commented May 30, 2017 at 0:01

2 Answers 2

2

You could do it this way which assumes that any path component comprised of all digit characters is a integer sequence index:

def size2(y, i):
     obj = i
     for comp in ((int(z) if z.isdigit() else z) for z in y.split('//')):
        obj = obj[comp]
     return obj

y = json2['y']
for i in json1['Items']:
    print(size2(y, i))  # this way now works

The size2() function could be made even more succinct by using the built-in reduce() function:

def size2(y, i):
    return reduce(lambda obj, v: obj[int(v) if v.isdigit() else v], y.split('//'), i)
Sign up to request clarification or add additional context in comments.

2 Comments

Yes, I also considered something like that. It will work as long as there are no string-digit-dict-keys. :)
@MSeifert: True. It's difficult to tell which of our answers would be more appropriate from the minimal information in the question. Your approach is probably more "Pythonic" in the sense that it uses EAFP instead of LBYL the way mine does.
2

As already pointed out in the comments, simply convert indices to integers before you index a sequence:

def size2(a,i):
    x = a.split('//')
    obj = i
    for part in x:  # using a loop you don't need to special case based on the length
        try:
            # Try to get it without converting to integer, for example to access dictionaries
            obj = obj[part]
        except TypeError:
            # Fallback to integer indexing
            obj = obj[int(part)]
    return obj

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.