- JSON format doesn't guarantee ordering or spacing so an API is free to keep the same semantics but rearrange fields. This will randomly break regular expression-based skins.
- Modification of and debugging non-trivial Regular Expressions is cumbersome, though there are some pretty cool tools for that.
- The inherent complexity of Regular Expressions puts this type of development out of reach for a lot of people.
- There are plenty of JSON parsing libraries that are well-tested and maintained and are performant.
I searched for a solution and didn't find one so I came up with my own. Here's a fully-functional skin using this technique. The essence follows.
Given this excerpt of the Darksky weather API JSON response:
Code: Select all
{
"timezone": "America/New_York",
"currently": {
"time": 1579389931,
"summary": "Light Snow",
"icon": "snow",
"temperature": 31.06,
"apparentTemperature": 25.12,
"dewPoint": 29.5,
"humidity": 0.94,
"pressure": 1018.1,
"visibility": 0.926,
"ozone": 300
},
"hourly": {
"summary": "Light rain until tomorrow morning.",
"icon": "rain",
"data": [
{
"time": 1579388400,
"summary": "Light Snow",
"icon": "snow",
"precipIntensity": 0.0516,
}
}
}
weather.ini
Code: Select all
[Weather]
Measure=WebParser
URL=https://api.darksky.net/forecast/#APIKey#/#Location#
RegExp=(?siU).*"currently":\{.*"summary":"([^"]*)"[^}]*"icon":"([^"]*)"[^}]*"temperature":([^\.]*)(\.|,).*}[^}]*}.*,"hourly":{"summary":"([^"]*)"
[Summary]
Measure=WebParser
URL=[Weather]
StringIndex=1
[Icon]
Measure=WebParser
URL=[Weather]
StringIndex=2
[Temperature]
Measure=WebParser
URL=[Weather]
StringIndex=3
[Hourly]
Measure=WebParser
URL=[Weather]
StringIndex=5
[SummaryMeter]
Meter=String
MeasureName=Summary
[IconMeter]
Meter=Image
MeasureName=Icon
ImageName=%1.png
[TemperatureMeter]
Meter=String
MeasureName=Temperature
[HourlyMeter]
Meter=String
MeasureName=Hourly
weather.ini
Code: Select all
[Weather]
Measure=WebParser
URL=https://api.darksky.net/forecast/#APIKey#/#Location#
RegExp=(?sU)^(.*)$
StringIndex=0
FinishAction=!UpdateMeasure ProcessData
[ProcessData]
Measure=Script
ScriptFile=#@#Scripts\weather.lua
UpdateDivider=-1
; Don't need measure sections :)
[Summary]
Meter=String
[Icon]
Meter=Image
[Temperature]
Meter=String
[Hourly]
Meter=String
Code: Select all
function Initialize()
dofile( SKIN:GetVariable( '@' )..'Scripts\\json.lua' )
weatherData = SKIN:GetMeasure( "Weather" )
end
function Update()
data = decodeData( weatherData:GetStringValue() )
SKIN:Bang( '!SetOption', 'Temperature', 'Text', data[ 'currently' ][ 'temperature' ] )
SKIN:Bang( '!SetOption', 'Icon', 'ImageName', "#@#Images\\" .. data[ 'currently' ][ 'icon' ] )
SKIN:Bang( '!SetOption', 'Summary', 'Text', data[ 'currently' ][ 'summary' ] )
SKIN:Bang( '!SetOption', 'Hourly', 'Text', data[ 'hourly' ][ 'summary' ] )
end
Get from https://github.com/rxi/json.lua
---
Questions and feedback are welcome.