dongxia4880
dongxia4880
2016-05-04 19:58

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 duanliao3826 5年前

    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,
                },
        }
    
    点赞 评论 复制链接分享

相关推荐