2017-01-19 44 views
2

我們如何動態地將子級XML與來自父級的屬性解組?使用來自父級的屬性動態解組子級XML

我們有以下個XML:

<!-- Report I --> 
<report type="YYYY-MM-DD"> 
    <created_at>2016-01-01</created_at> 
</report> 

<!-- Report II --> 
<report type="DD-MM-YYYY"> 
    <created_at>01-01-2016</created_at> 
</report> 

,我們有以下結構:

type Report struct { 
    XMLName xml.Name `xml:"report"` 
    Type  string  `xml:"type,attr"` 
    CreatedAt *ReportDate `xml:"created_at"` 
} 

type ReportDate struct { 
    time.Time 
} 

func (c *ReportDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    const format = "02-01-2006" // or "2016-01-02" depending on parent's "type" 
    var v string 
    d.DecodeElement(&v, &start) 
    parse, err := time.Parse(format, v) 
    if err != nil { 
     return err 
    } 
    *c = ReportDate{parse} 
    return nil 
} 

,纔有可能爲ReportDate獲得type="?"從它的父在UnmarshalXML?或者Report可能會將屬性值傳遞給所有子標籤?如果可能的話,我們該如何做到這一點?

回答

1

解析父項時,可以在子元素中設置一個'private'字段,讓它知道要使用的時間格式字符串。

下面是一個工作示例https://play.golang.org/p/CEqjWoDQR3

而這裏的代碼:

package main 

import (
    "encoding/xml" 
    "fmt" 
    "io" 
    "time" 
) 

// TypeMap converts the XML date format string to a valid Go date format string 
var typeMap = map[string]string{ 
    "YYYY-MM-DD": "2006-01-02", 
    "DD-MM-YYYY": "02-01-2006", 
} 

// Example XML documents 
var reportStrings = []string{ 
    `<!-- Report I --> 
<report type="YYYY-MM-DD"> 
    <created_at>2016-01-01</created_at> 
</report>`, 

    `<!-- Report II --> 
<report type="DD-MM-YYYY"> 
    <created_at>01-01-2016</created_at> 
</report>`, 
} 

type Report struct { 
    XMLName xml.Name `xml:"report"` 
    Type  string  `xml:"type,attr"` 
    CreatedAt ReportDate `xml:"created_at"` 
} 

type ReportDate struct { 
    dateFormatStr string // lower-case field is ignored by decoder/encoder 

    Time time.Time 
} 

func (r *Report) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    for _, attr := range start.Attr { 
     if attr.Name.Local == "type" { 
      dateFormatStr, ok := typeMap[attr.Value] 
      if !ok { 
       return fmt.Errorf("unknown date type '%s'", attr.Value) 
      } 
      r.CreatedAt.dateFormatStr = dateFormatStr 
     } 
    } 

    for { 
     tok, err := d.Token() 
     if err == io.EOF { 
      break 
     } 
     if err != nil { 
      return err 
     } 
     switch tok.(type) { 
     case xml.StartElement: 
      nextStart := tok.(xml.StartElement) 
      if nextStart.Name.Local == "created_at" { 
       d.DecodeElement(&r.CreatedAt, &nextStart) 
      } 
     } 
    } 

    return nil 
} 

func (c *ReportDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    var s string 
    d.DecodeElement(&s, &start) 
    t, err := time.Parse(c.dateFormatStr, s) 
    if err != nil { 
     return err 
    } 
    c.Time = t 
    return nil 
} 

func main() { 
    for i, reportStr := range reportStrings { 
     var report Report 
     if err := xml.Unmarshal([]byte(reportStr), &report); err != nil { 
      panic(err) 
     } 

     fmt.Printf("[%d] %s\n", i, report.CreatedAt.Time) 
    } 
} 
+0

不錯!如果報告中有其他元素,例如,'令人敬畏的報告'。我們如何在'UnmarshalXML'中傳遞它們? –

+0

看到我的其他答案。 –

1

我不知道是否有對應Golang什麼更地道,但是......

如果您加入了更多元素來報告(如在「名稱」 )的代碼是這樣的:

https://play.golang.org/p/5VpzXM5F95

package main 

import (
    "encoding/xml" 
    "fmt" 
    "io" 
    "time" 
) 

var typeMap = map[string]string{ 
    "YYYY-MM-DD": "2006-01-02", 
    "DD-MM-YYYY": "02-01-2006", 
} 

var reportStrings = []string{ 
    `<!-- Report I --> 
<report type="YYYY-MM-DD"> 
    <created_at>2016-01-01</created_at> 
    <name>Awesome Report I</name> 
</report>`, 

    `<!-- Report II --> 
<report type="DD-MM-YYYY"> 
    <created_at>01-01-2016</created_at> 
    <name>Awesome Report II</name> 
</report>`, 
} 

type Report struct { 
    XMLName xml.Name `xml:"report"` 
    Type string `xml:"type,attr"` 

    Name  string  `xml:"name"` 
    CreatedAt ReportDate `xml:"created_at"` 
} 

type ReportDate struct { 
    dateFormatStr string // lower-case field is ignored by decoder/encoder 

    Time time.Time 
} 

func (r *Report) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    for _, attr := range start.Attr { 
     if attr.Name.Local == "type" { 
      r.Type = attr.Value 
      dateFormatStr, ok := typeMap[attr.Value] 
      if !ok { 
       return fmt.Errorf("unknown date type '%s'", attr.Value) 
      } 
      r.CreatedAt.dateFormatStr = dateFormatStr 
     } 
    } 

    for { 
     tok, err := d.Token() 
     if err == io.EOF { 
      break 
     } 
     if err != nil { 
      return err 
     } 
     switch tok.(type) { 
     case xml.StartElement: 
      nextStart := tok.(xml.StartElement) 
      local := nextStart.Name.Local 
      if local == "created_at" { 
       d.DecodeElement(&r.CreatedAt, &nextStart) 
      } else if local == "name" { 
       d.DecodeElement(&r.Name, &nextStart) 
      } 
     } 
    } 

    return nil 
} 

func (c *ReportDate) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { 
    var s string 
    d.DecodeElement(&s, &start) 
    t, err := time.Parse(c.dateFormatStr, s) 
    if err != nil { 
     return err 
    } 
    c.Time = t 
    return nil 
} 

func main() { 
    for i, reportStr := range reportStrings { 
     var report Report 
     if err := xml.Unmarshal([]byte(reportStr), &report); err != nil { 
      panic(err) 
     } 

     fmt.Printf("[%d] %v\n", i, report) 
    } 
} 
相關問題