Seeking a simple JSON parse example

I'm using an asyncHTTPget to pull json from the US navy. This json is ultra simple. https://aa.usno.navy.mil/api/seasons?year=2023&tz=-6&dst=true
which returns:
{
"apiversion": "4.0.1",
"data": [
{
"day": 4,
"month": 1,
"phenom": "Perihelion",
"time": "10:17 ST",
"year": 2023
},
{
"day": 20,
"month": 3,
"phenom": "Equinox",
"time": "16:24 DT",
"year": 2023
},
{
"day": 21,
"month": 6,
"phenom": "Solstice",
"time": "09:58 DT",
"year": 2023
},
{
"day": 6,
"month": 7,
"phenom": "Aphelion",
"time": "15:06 DT",
"year": 2023
},
{
"day": 23,
"month": 9,
"phenom": "Equinox",
"time": "01:50 DT",
"year": 2023
},
{
"day": 21,
"month": 12,
"phenom": "Solstice",
"time": "21:27 ST",
"year": 2023
}
],
"dst": true,
"tz": -6.0,
"year": 2023
}

The examples to parse JSON are not helping me much.

I have to do this since HE doesn't allow external libs (import net.time4lcalendar would solve my problems!)
(the routine I need is here (an extended date/time with astronomical built in):

Not quite sure I follow... Is the issue parsing the JSON or trying to construct a date/time from the data returned?

Why use async GET? Just use the sync version (httpGet()) and the response will likely be auto parsed into a Map of the json response.

Here's a demo that does it both ways. Just pull the data you need from the Map in either case:

EDIT: updated example code here: Seeking a simple JSON parse example - #9 by tomw

2 Likes

i stumple over breaking the array apart from the json.. but I see a post after yours that may have simplified it perfectly! Thank you all.

1 Like

Not sure I understand.

Are you looking for something like this (using my same variable names as above):

resp.data?.data?.getAt(0)?.time

What output do you want to create from the response found from that URL?

Yes! its the variable naming of the sub sections within the JSON i've never understood. From the JSON example I posted, I'm trying to pull the equinox and Solstice values to create an Astronimical Season app. My meteoroligical map bugs me!

Ah, in that case, you can get a little bit more fancy.

Try this. The magic is in parseResp().

EDIT: updated example code here: Seeking a simple JSON parse example - #11 by tomw

Just making sure you saw this @jshimota. Does it help?

1 Like

@tomw - since you basically wrote it for me - it was simple enough to use. thank you. I'm still unclear how to target the specific object tho. At this:
["Equinox","Solstice",].each
the result gives me the first instance of the phenom value. how to target the 2nd one or store each into thier own variable?
I put :
if(phenom) sendEvent(name: "firstEquinox", value: "${it}: ${phenom}", descriptionText: descriptionText) else sendEvent(name: "firstEquinox", value: "No Data", descriptionText: descriptionText) which works great - but I can't determine how to yank the 2nd iteration.

I don't know exactly what data and format you need, but you could do something like this:

/*

 */

metadata 
{
    definition( name: "json resp demo", namespace: "tomw", author: "tomw", singleThreaded: true ) 
    {
        command "trace"
    }
}

def trace()
{
    def url = "https://aa.usno.navy.mil/api/seasons?year=2023&tz=-6&dst=true"

    def res = syncop(url)
    parseResp(res)

    asyncop(url)
}

def parseResp(Map res)
{
    def phenom
    ["Equinox", "Solstice"].each
    {
        phenom = res?.data?.findAll { phe -> phe?.phenom == it }
        
        if(phenom)
        {
            phenom.eachWithIndex
            { thisone, idx -> 
                sendEvent(name: it+idx, value: "${thisone.month}:${thisone.day}:${thisone.year}")
            }
            
            //log.debug "${it}: ${phenom}"
        }
    }
    
}

def syncop(url)
{
    def res
    httpGet(url)
    {
        resp->
        log.debug "sync respose: ${resp.data}"
        res = resp.data
    }
    
    return res
}

def asyncop(url)
{
    asynchttpGet('theCallback', [uri: url])
}

def theCallback(resp, data)
{
    if(resp.data)
    {
        def res = new groovy.json.JsonSlurper().parseText(resp.data)
        log.debug "async response: ${res}"
        
        parseResp(res)
    }
}

1 Like

ahhh findAll - I'd love to live inside your skull for a month. I might learn something!

2 Likes

Why not use async? Either way you just use getJson() to parse the output.

Either works. But in this case IMHO the response is going to be basically instant and using asyc just adds unnecessary complexity.

If you're going over the Internet an instant response is never guaranteed. Plus, IMHO, keeping the data parsing in a separate function from the API call is nicer organizationally.

Fair enough on both points. FWIW my example shows both ways.

One thing that is harder is passing data back to the caller (if needed) from the async parse function. I really wish Groovy and/or Hubitat had an await-like semantic.

1 Like
	def pData = [cd:[cd]]
	if (debugOutput) log.debug "sending getStatus request $params"
	asynchttpGet("getStatusHandler", params, pData)
}

def getStatusHandler (resp, data) {
	try {
		def cdd = data["cd"]

That's a snippet from my Honeywell Thermostat. "cd" is "child device"

It's simple enough, making me think that's not what you mean. :slight_smile:

Maybe the documentation is better now than when I first had to figure it out. :smiley:

That direction for passing data is straightforward (though the docs have definitely improved in the meantime :wink: ).

My point is that if you need data from getStatusHandler back in the main calling function, it's complicated. Whereas with a synchronous httpGet you get the data directly.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.