dongxia4880 2016-05-04 19:58
浏览 125
已采纳

XML转换为JSON多重嵌套

I'm attempting to write code to translate XML to JSON. The XML I'm trying to translate is as follows...

(Just a snippet)

`<version>0.1</version>
    <termsofService>http://www.wunderground.com/weather/api/d/terms.html</termsofService>
    <features>
        <feature>conditions</feature>
    </features>
  <current_observation>
        <image>
        <url>http://icons.wxug.com/graphics/wu2/logo_130x80.png</url>
        <title>Weather Underground</title>
        <link>http://www.wunderground.com</link>
        </image>
        <display_location>
        <full>Kearney, MO</full>
        <city>Kearney</city>
        <state>MO</state>
        <state_name>Missouri</state_name>`

Current Code:

`package main

import (
    "fmt"
    "net/url"
    "encoding/xml"
    "net/http"
    "log"
    "io/ioutil"
    "encoding/json"
)

type reportType struct{
    Version xml.CharData        `xml:"version"`
    TermsOfService xml.CharData `xml:"termsofService"
    `
    Features xml.CharData       `xml:"features>feature"`
    Full     xml.CharData       `xml:"current_observation>display_location>full"`
    StateName xml.CharData      `xml:"current_observation>display_location>state_name"`
    WindGust xml.CharData       `xml:"current_observation>observation_location>full"`
    Problem myErrorType     `xml:"error"`
}
type myErrorType struct{
    TypeOfError xml.CharData `xml:"type"`
    Desciption xml.CharData `xml:"description"`
}
type reportTypeJson struct{
    Version        string  `json:"version"`;
    TermsOfService string `json:"termsofService"`;
    Features    map[string]string `json:"features"`;
    Full        map[string]string `json:"display_location"`;
    WindGust map[string]string `json:"observation_location"`

}
func main() {
    fmt.Println("data is from WeatherUnderground.")
    fmt.Println("https://www.wunderground.com/")
    var state, city string
    str1 := "What is your state?"
    str2 := "What is your city?"
    fmt.Println(str1)
    fmt.Scanf("%s", &state)
    fmt.Println(str2)
    fmt.Scanf("%s", &city)
    baseURL := "http://api.wunderground.com/api/";
    apiKey := "3hunna"
    var query string

    //set up the query
    query = baseURL+apiKey +
    "/conditions/q/"+
    url.QueryEscape(state)+ "/"+
    url.QueryEscape(city)+ ".xml"
    fmt.Println("The escaped query: "+query)

    response, err := http.Get(query)
    doErr(err, "After the GET")
    var body []byte
    body, err = ioutil.ReadAll(response.Body)
    doErr(err, "After Readall")
    fmt.Println(body);
    fmt.Printf("The body: %s
",body)

    //Unmarshalling
    var report reportType
    xml.Unmarshal(body, &report)
    fmt.Printf("The Report: %s
", report)
    fmt.Printf("The description is [%s]
",report.Problem.Desciption)

    //Now marshal the data out in JSON
    var data []byte
    var output reportTypeJson
    output.Version = string(report.Version);
    output.TermsOfService = string(report.TermsOfService)

    output.Features= map[string]string{"feature":string(report.Features)} // allocate a map, add the 'features' value to it and assign it to output.Features
    output.Full=map[string]string{"full":string(report.Full),"state_name":string(report.StateName)}
    output.WindGust=map[string]string{"full":string(report.WindGust)}
    data,err = json.MarshalIndent(output,"","      ")
    doErr(err, "From marshalIndent")
    fmt.Printf("JSON output nicely formatted: 
%s
",data)


}
func doErr( err error, message string){
    if err != nil{
        log.Panicf("ERROR: %s %s 
", message, err.Error())
    }


}

As you can see, I'm using maps to map one level nesting such as in the features case. But for in two level nesting cases such as xml:"current_observation>display_location>state_name", I can't figure out how to create the very first level, in this case current_observations. Would there be a way to somehow create a map of maps of sorts? Any and all ideas are much appreciated because I am very confused at the moment, Thanks for your time!

And the Output:

JSON output nicely formatted: 
{
      "version": "0.1",
      "termsofService": "http://www.wunderground.com/weather/api/d/terms.html",
      "features": {
            "feature": "conditions"
      },
      "display_location": {
            "full": "Kearney, MO",
            "state_name": "Missouri"
      },
      "observation_location": {
            "full": "Stonecrest, Kearney, Missouri"
      }
}
  • 写回答

1条回答 默认 最新

  • duanliao3826 2016-05-04 20:12
    关注

    You could use either structs or a map of maps. I'll give some examples of both, starting with the map of maps. The type would be declares as;

    CurrentObservation map[string]map[string]string `json:"current_observation"`
    

    In this case you have a map with strings as the keys and the value is another map that has string for both key and value. As a result when you marshal your json you will end up with something like;

    "current_observation" {
         "image": { // first level where the value is a map[string]string
              "title":"Weather Underground" // inner map, string to string
          }
    }
    

    If say you wanted to just print the title, you would do;

    fmt.Println(reportJson.CurrentObservation["image"]["title"])
    

    Since the data there looks fairly static you could also use structs instead. In which case you'd use something like this;

    CurrentObservation CurrentObservation `json:"current_observation"`
    
    type CurrentObservation struct {
        Image Image `json:"image"`
    }
    
    type Image struct {
        Url string `json:"url"`
        Title string `json:"title"`
        Link string `json:"link"`
    }
    

    Both options produce the same output though they could behave differently for different inputs. For example, if another version of current_observation were received as input that for example has another nested item in it call it... previous_observation then the map option would automatically unmarhsal this data as well where the struct options would exclude it since there would be no mapping to any object/type in Go.

    Personally I prefer the struct route when possible but it varies from case to case. For your application the map is likely better since you're not working with the input (it comes in as xml) and you just want to print it, you don't really have to deal with the details of current_observation, if it has 3 objects inside it, they'll all be output as expected, if it were 5 it would be the same. With the structs you have to explicitly define every single field which isn't really necessary if you're just transforming the input. The advantage of the struct is more for use later on where you have type safety although in this case, I would say they're still fairly equivalent because for example anytime you want to access something in image, like CurrentObservation.Image.Title you'll have to perform a check to ensure Image is not nil, like;

    if CurrentObservation.Image != nil {
        fmt.Println(CurrentObservation.Image.Title)
    }
    

    With the map you basically have the same overhead, only you're checking for the existence of a key rather than checking if one of the inner structs is nil or not.

    EDIT: example of initializing the map of maps using composite literal syntax;

       reportJson.CurrentObservation := map[string]map[string]string {
               "display_location": map[string]string {
                    "full": report.Full,
                    "state_name": report.StateName,
                },
        }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 安卓adb backup备份应用数据失败
  • ¥15 eclipse运行项目时遇到的问题
  • ¥15 关于#c##的问题:最近需要用CAT工具Trados进行一些开发
  • ¥15 南大pa1 小游戏没有界面,并且报了如下错误,尝试过换显卡驱动,但是好像不行
  • ¥15 没有证书,nginx怎么反向代理到只能接受https的公网网站
  • ¥50 成都蓉城足球俱乐部小程序抢票
  • ¥15 yolov7训练自己的数据集
  • ¥15 esp8266与51单片机连接问题(标签-单片机|关键词-串口)(相关搜索:51单片机|单片机|测试代码)
  • ¥15 电力市场出清matlab yalmip kkt 双层优化问题
  • ¥30 ros小车路径规划实现不了,如何解决?(操作系统-ubuntu)