Golang自定义解组XML

时间:2018-10-26 10:06:22

标签: xml go

我一直在尝试编写一个自定义的Unmarshal来处理一些XML代码。

我知道您可以编写一个自定义解组方法来处理结构中的单个字段,但是我试图在整个结构上编写一个方法:

type LowestPricedPricedOffers struct {
ASIN   string `xml:"GetLowestPricedOffersForASINResult>Identifier>ASIN"`
TimeOfOfferChange string  `xml:"GetLowestPricedOffersForASINResult>Identifier>TimeOfOfferChange"`
TotalOfferCount int   `xml:"GetLowestPricedOffersForASINResult>Summary>TotalOfferCount"`
ListPrice         float32 `xml:"GetLowestPricedOffersForASINResult>Summary>ListPrice>Amount"`
LowestNew         float32 //lowest new landed price
LowestUsed        float32 //lowest used landed price
OfferCount        map[string]map[string]int
CurrencyCode      string //currency code taken from LowestPrices
NumberOfOffers    []struct {
    OfferCount         string `xml:",innerxml"` //Will not work with int
    Condition          string `xml:"condition,attr"`
    FulfillmentChannel string `xml:"fulfillmentChannel,attr"`
} `xml:"GetLowestPricedOffersForASINResult>Summary>NumberOfOffers>OfferCount"`
LowestPrices []struct {
    Condition          string         `xml:"condition,attr"`
    FulfillmentChannel string         `xml:"fulfillmentChannel,attr"`
    LandedPrice        float32Unmarsh `xml:"LandedPrice>Amount"`
    ListingPrice       float32        `xml:"ListingPrice>Amount"`
    Shipping           float32        `xml:"Shipping>Amount"`
    CurrencyCode       string         `xml:"LandedPrice>CurrencyCode"`
} `xml:"GetLowestPricedOffersForASINResult>Summary>LowestPrices>LowestPrice"`
}


func (n *LowestPricedPricedOffers) UnmarshalXML(d *xml.Decoder, start    xml.StartElement) error {
for {
    t, err := d.Token()
    if err == io.EOF {
        break
    }

    switch tt := t.(type) {
    case xml.StartElement:
        var value int
        if tt.Name.Local == "OfferCount" {
            d.DecodeElement(&value, &start)
            var condition string
            var fullfillmentChannel string
            for _, x := range tt.Attr {
                //fmt.Println(x.Name.Local, x.Value)
                if x.Name.Local == "condition" {
                    condition = x.Value
                    fmt.Println(condition)

                } else {
                    fullfillmentChannel = x.Value
                }

            }
            if n.OfferCount == nil {
                n.OfferCount = make(map[string]map[string]int)
                n.OfferCount[condition] = make(map[string]int)
                n.OfferCount[condition][fullfillmentChannel] = value

            } else {
                if n.OfferCount[condition] == nil {
                    n.OfferCount[condition] = make(map[string]int)
                }
                n.OfferCount[condition][fullfillmentChannel] = value
            }

        } else if tt.Name.Local == "ASIN" {
            d.DecodeElement(&n.ASIN, &start)
        }

        // More else if's to deal with rest of XML

    }
}
return nil
}

这似乎起作用,因为我可以控制如何填充结构,例如,生成“ OfferCount”的映射。使用我的通用方法,然后我需要使用例如

来手动填充其余结构
} else if tt.Name.Local == "ASIN" {
        d.DecodeElement(&n.ASIN, &start)
    }

我将不得不处理XML中的所有元素,必须为每个元素指定struct字段。有没有一种方法可以覆盖某些元素,但仅按照标准解组利用结构中的标记自动填充其余元素,而无需为结构字段生成单独的类型?

我已将完整代码张贴在go playground上:

我希望这有道理。

我已经更新了方法和问题,以在其上使用自定义类型和自定义解组。

主要结构已更新如下:

type LowestPricedPricedOffers struct {

Error AmazonError `xml:"Error"`
All   struct {

        /*The only way I found to retrieve 'status' from the GetLowestPricedOffersForASINResult element was to wrap in the struct 'All'.
        This is the only reason the All struct exists.  Ideally would like to remove*/
        Status            string     `xml:"status,attr"` 

    ASIN              string     `xml:"Identifier>ASIN"`
    ItemCondition     string     `xml:"Identifier>ItemCondition"`
    TimeOfOfferChange string     `xml:"Identifier>TimeOfOfferChange"`
    TotalOfferCount   int        `xml:"Summary>TotalOfferCount"`
    ListPrice         float32    `xml:"Summary>ListPrice>Amount"`
    OfferCount        offerCount `xml:"Summary>NumberOfOffers"`

    //Want to take Currency code from LowestPrices below but cannot think of a way to achieve this against the lowestPrices type
    //CurrencyCode         string               `xml:"CurrencyCode"` 

    BuyBoxPrices         buyBoxPrices         `xml:"Summary>BuyBoxPrices"`
    LowestPrices         lowestPrices         `xml:"Summary>LowestPrices"`
    BuyBoxEligibleOffers buyBoxEligibleOffers `xml:"Summary>BuyBoxEligibleOffers"`

    Offers []struct {
        SubCondition                 string  `xml:"SubCondition"`
        SellerPositiveFeedbackRating float32 `xml:"SellerFeedbackRating>SellerPositiveFeedbackRating"`
        FeedbackCount                int     `xml:"SellerFeedbackRating>FeedbackCount"`
        ShippingTime                 struct {
            MinimumHours     int    `xml:"minimumHours,attr"`
            MaximumHours     int    `xml:"maximumHours,attr"`
            AvailabilityType string `xml:"availabilityType,attr"`
        }
        ListingPrice        float32 `xml:"ListingPrice>Amount"`
        Shipping            float32 `xml:"Shipping>Amount"`
        ShipsFrom           string  `xml:"ShipsFrom>Country"`
        IsFulfilledByAmazon bool    `xml:"IsFulfilledByAmazon"`
        IsBuyBoxWinner      bool    `xml:"IsBuyBoxWinner"`
        IsFeaturedMerchant  bool    `xml:"IsFeaturedMerchant"` //true if the seller of the item is eligible to win the Buy Box.
    } `xml:"Offers>Offer"`
} `xml:"GetLowestPricedOffersForASINResult"`

}

它包括“全部”结构,这似乎是从GetLowestPricedOffersForASINResult元素中解组“状态”的唯一方法。理想情况下,我想摆脱这种包装器结构,但是我不确定这是可能的。

一些自定义类型是地图,以便于轻松检索数据:

type offerCount map[string]map[string]int

func (m *offerCount) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {

var content map[string]map[string]int
for {
    t, err := d.Token()
    if err == io.EOF {
        break
    }
    switch tt := t.(type) {
    case xml.StartElement:
        var value int
        if tt.Name.Local == "OfferCount" {
            d.DecodeElement(&value, &start)
            var condition string
            var fullfillmentChannel string
            for _, x := range tt.Attr {

                if x.Name.Local == "condition" {
                    condition = x.Value

                } else {
                    fullfillmentChannel = x.Value

                }

            }

            if content == nil {
                content = make(map[string]map[string]int)
                content[condition] = make(map[string]int)
                content[condition][fullfillmentChannel] = value

            } else {
                if content[condition] == nil {
                    content[condition] = make(map[string]int)
                }
                content[condition][fullfillmentChannel] = value
            }


        }
    }
    *m = offerCount(content)
}
return nil

}

我还在尝试找出如何通过lowestPrices类型设置CurrencyCode,不确定是否可行?

完整的更新代码位于go playground上,以征询一般性意见或建议。

0 个答案:

没有答案