def treeGet(obj_to_search, json_path, defaultAnswer=None):
    """This function was developed to provide a simple way to traverse python object trees but discovered that jmespath provided 99% of the functionality so it has been changed to simply use that functionality.
    
    JMESPath does lack the support for providing a default answer if the search, so this does still introduce some additional functionality
    
    :param obj_to_search: Python object representing the structure to search.
    :type obj_to_search: dict, list or scalar
    :param json_path: simplified query string indicating what object branch to return
    :type json_path: str
    :return: The branch of obj_to_search indicated by json_path
    :rtype: any, based on the object in obj_to_search pointed to by json_path
    
    """
    from jmespath import search as jsearch
    try:
        r = jsearch(json_path, obj_to_search)
        if r is None:
            return defaultAnswer
        return r
    except:
        return defaultAnswer
    """
    This function helps to navigate structures of lists and dictionaries with the same functionality of the get method of a dictionary without having to string a series of objects together.
    
    Short Example based on myTree below:
        classic python: 
            myTree.get('list',[None,{}])[1].get('test','unknown')
        treeGet python
            treeGet(myTree,'list[1].test','unknown')
            
        In the classic case, the default for list must contain a default answer capable of getting to the suceeding get (which means a dict). If we had been exploring element 10 the default answer would have been more complicated
    
    Example - Given an object structured as follows:
        myTree = {
            'dict': {
                'one': 1,
                'two': '2',
                'three': False
                },
                'list': [
                    'a',
                    { 'test': 'answer'},
                    3
                ],
                'string': 'test'
        }
        
        accessing test to retrieve answer would look something like the following.
            a = myTree.get('dict',{}).get('list',[None,None])[1].get('test','defaultanswer')
            
        Depending on how many items were expected in the "list" the default answer of [None,None] would have to be expanded to prevent the following [1] from failing. Also typically people would wrap this with a try/except to catch any unexpected failures
        
            try:
                a = myTree.get('dict',{}).get('list',[None,None])[1].get('test','defaultanswer')
            except:
                a = 'defaultanswer'
            
        This function provides all of the above as well as cloning each of the items as the tree is traversed so the return object is mutable and un-tied from the object from which it originated
    
            a = utils.treeGet(myTree, 'dict.list[1].test', 'defaultanswer')
            
    """
    tree = json_path.split('.')
    for nextbranch in tree:
        if isinstance(obj_to_search, dict):
            obj_to_search = dict(obj_to_search)
        if isinstance(obj_to_search, list):
            obj_to_search = list(obj_to_search)
        index = None
        if nextbranch.startswith('['):
            if not isinstance(obj_to_search, list):
                return defaultAnswer
            index = int(nextbranch.split('[')[1].split(']')[0])
        elif nextbranch.endswith(']'):
            obj_to_search = obj_to_search.get(nextbranch.split('[', 1)[0], None
                )
            alphaindex = nextbranch.split('[', 1)[1].split(']', 1)[0]
            if ':' in alphaindex:
                return defaultAnswer
            index = int(alphaindex)
        else:
            obj_to_search = obj_to_search.get(nextbranch, None)
        if obj_to_search is None:
            return defaultAnswer
        if index is not None:
            if not isinstance(obj_to_search, list):
                return defaultAnswer
            if index > -1 and len(obj_to_search) < index:
                return defaultAnswer
            elif index < 0 and len(obj_to_search) < abs(index):
                return defaultAnswer
            obj_to_search = obj_to_search[index]
    return obj_to_search
