[笔记]Go语言实现同一结构体适配多种消息源
问题:
提供天气信息的网站有很多,每家的数据及格式都不同,为了适配各种不同的天气接口,写了如下程序。
代码如下:
package main import ( "encoding/json" "errors" "fmt" "regexp" "strconv" "strings" ) var s string = ` { "error": 0, "status": "success", "date": "2015-03-26", "results": [ { "test": [ [ "fuck", "shit" ], { "ao": "xxx" }, "vfe" ], "currentCity": "郑州", "pm25": "95", "index": [ { "title": "穿衣", "zs": "较冷", "tipt": "穿衣指数", "des": "建议着厚外套加毛衣等服装。年老体弱者宜着大衣、呢外套加羊毛衫。" }, { "title": "洗车", "zs": "不宜", "tipt": "洗车指数", "des": "不宜洗车,未来24小时内有雨,如果在此期间洗车,雨水和路上的泥水可能会再次弄脏您的爱车。" }, { "title": "旅游", "zs": "适宜", "tipt": "旅游指数", "des": "温度适宜,又有较弱降水和微风作伴,会给您的旅行带来意想不到的景象,适宜旅游,可不要错过机会呦!" }, { "title": "感冒", "zs": "少发", "tipt": "感冒指数", "des": "各项气象条件适宜,无明显降温过程,发生感冒机率较低。" }, { "title": "运动", "zs": "较不宜", "tipt": "运动指数", "des": "有降水,推荐您在室内进行健身休闲运动;若坚持户外运动,须注意保暖并携带雨具。" }, { "title": "紫外线强度", "zs": "最弱", "tipt": "紫外线强度指数", "des": "属弱紫外线辐射天气,无需特别防护。若长期在户外,建议涂擦SPF在8-12之间的防晒护肤品。" } ], "weather_data": [ { "date": "周四 03月26日 (实时:12℃)", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/zhenyu.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png", "weather": "阵雨转多云", "wind": "微风", "temperature": "12 ~ 4℃" }, { "date": "周五", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/qing.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/qing.png", "weather": "晴", "wind": "微风", "temperature": "16 ~ 8℃" }, { "date": "周六", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/qing.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/duoyun.png", "weather": "晴转多云", "wind": "微风", "temperature": "22 ~ 9℃" }, { "date": "周日", "dayPictureUrl": "http://api.map.baidu.com/images/weather/day/qing.png", "nightPictureUrl": "http://api.map.baidu.com/images/weather/night/qing.png", "weather": "晴", "wind": "微风", "temperature": "25 ~ 11℃" } ] } ] } ` // 天气 type Weather struct { Error_Code string `json:"error_code"` //错误码 0没有错误 Status string `json:"status"` // 状态描述, success 成功 Result Result `json:"result"` // 天气状况 } // 天气结果 type Result struct { Now Real LivingIndex []Indicate `json:"living_index"` //生活指数 Future []ForeCast `json:"future"` //未来几天的预报,包括当天 } // 实时报告 type Real struct { City string `json:"city"` // 城市 Temperature string `json:"temperature"` // 当前温度 WindDirection string `json:"wind_direction"` // 风向 WindStrength string `json:"wind_strength"` // 风强度 Humidity string `json:"humidity"` // 湿度 PM25 string `json:"pm25"` // PM2.5 } // 预报 type ForeCast struct { Temperature string `json:"temperature"` // 温度范围 Weather string `json:"weather"` // 天气 (晴,多云,阴转多云) Wind string `json:"wind"` // 风向和强度 Week string `json:"week"` // 星期几 Date string `json:"date"` // 日期 } type Indicate struct { Title string `json:"titile"` // 指数标题 Status string `json:"status"` //指数状态 (适宜,不宜) Description string `json:"desc"` //描述信息 } func NewWeather() *Weather { return NewWeatherWithNum(6, 4) } func NewWeatherWithNum(livingIndexNum, forecastNum int) *Weather { w := &Weather{} w.Result.LivingIndex = make([]Indicate, livingIndexNum) w.Result.Future = make([]ForeCast, forecastNum) return w } type ElementExtractor struct { Err error m map[string]interface{} r *regexp.Regexp } func NewElementExtractor(jsonString string) *ElementExtractor { ee := &ElementExtractor{r: regexp.MustCompile(`(.*)\[(\d+)\]+`)} ee.Err = json.Unmarshal([]byte(jsonString), &ee.m) return ee } func (ee *ElementExtractor) ExtractElementByPattern(patten string) (string, error) { if ee.Err != nil { return "", ee.Err } var mm map[string]interface{} = ee.m var sa []interface{} patten = strings.Replace(patten, "][", "].[", -1) for _, k := range strings.Split(patten, ".") { ki := ee.r.FindStringSubmatch(k) if len(ki) == 3 { j, _ := strconv.Atoi(ki[2]) if ki[1] != "" { sa = mm[ki[1]].([]interface{}) } switch s := sa[j].(type) { case string: return s, nil case map[string]interface{}: mm = s case []interface{}: sa = sa[j].([]interface{}) default: return fmt.Sprintf("%v", s), nil } } else { switch s := mm[k].(type) { case string: return s, nil case map[string]interface{}: mm = s default: return fmt.Sprintf("%v", s), nil } } } ee.Err = errors.New("Pattern Error: " + patten) return "", ee.Err } func main() { ee := NewElementExtractor(s) w := NewWeather() w.Status, _ = ee.ExtractElementByPattern("status") w.Error_Code, _ = ee.ExtractElementByPattern("error") w.Result.Now.City, _ = ee.ExtractElementByPattern("results[0].currentCity") w.Result.Now.PM25, _ = ee.ExtractElementByPattern("results[0].pm25") w.Result.Future[2].Date, _ = ee.ExtractElementByPattern("results[0].weather_data[2].date") w.Result.LivingIndex[0].Title, _ = ee.ExtractElementByPattern("results[0].index[0].title") w.Result.Future[2].Temperature, _ = ee.ExtractElementByPattern("results[0].weather_data[2].temperature") w.Result.Now.PM25, _ = ee.ExtractElementByPattern("results[0].test[0][1]") w.Result.Now.WindDirection, _ = ee.ExtractElementByPattern("results[0].test[1].ao") w.Result.Now.WindStrength, _ = ee.ExtractElementByPattern("results[0].test[2]") if ee.Err != nil { fmt.Println(ee.Err) } else { fmt.Println(w) } }
运行结果:
&{0 success {{郑州 xxx vfe shit} [{穿衣 } { } { } { } { } { }] [{ } { } {22 ~ 9℃ 周六} { }]}}
爱生活,爱拉风