Golang将XML属性解组为接口

huangapple go评论94阅读模式
英文:

Golang unmarshal XML attribute into interface

问题

我正在尝试将一些XML解组成一个具有interface{}类型的结构体。然而,无论我如何运行代码,它都没有解析出任何内容。所有其他元素似乎都正常工作,如果我将类型设置为string或[]byte,它将正常工作,但是我需要它更加灵活。

我感兴趣的元素位于第32行 - FloorRefID

以下是XML示例:

<?xml version="1.0" encoding="UTF-8"?>
<Locations totalPages="1" currentPage="1" pageSize="25">
   <WirelessClientLocation macAddress="00:00:00:00:00:00">
      <MapInfo mapHierarchyString="Head office&gt;Ground floor&gt;Store" floorRefId="-1122334455667789">
         <Image imageName="floorPlan1.png" />
      </MapInfo>
      <MapCoordinate x="2850" y="3000" unit="FEET" />
   </WirelessClientLocation>
   <WirelessClientLocation macAddress="11:11:11:11:11:11">
      <MapInfo mapHierarchyString="Head office&gt;Ground floor&gt;Store" floorRefId="-1122334455667789">
         <Image imageName="floorPlan1.png" />
      </MapInfo>
      <MapCoordinate x="10.72" y="76.49" unit="FEET" />
   </WirelessClientLocation>
   <WirelessClientLocation macAddress="26:cd:96:46:0b:2b">
      <MapInfo floorRefId="0" />
      <MapCoordinate x="51.52" y="4.2" unit="FEET" />
   </WirelessClientLocation>
</Locations>

为了提供一些背景信息:我正在与一个供应商集成的项目上工作,有时我们会收到XML格式的数据,有时是JSON格式的数据。我希望构建一个可以解组两种格式的结构体的工具,而不是复制相同结构的代码。这个结构体有很多子结构,这意味着如果要保持两个几乎相同的结构体,除了这个属性之外,需要做很多工作。

当我们收到JSON数据时,该字段可以是字符串或数字。

我了解到不能将数据解组到interface{}类型中,但是否有人知道如何解决我这种情况的问题呢?

英文:

I'm trying to unmarshal some XML into a structure with an interface{} type. However whenever I try to run it, the code doesn't pick up anything at all. All of the other elements appear to work fine, and if I set the type to string or []byte it will work, however I need it to be more flexible than that.

The element I am interested in is on <b>line 32</b> - <b>FloorRefID</b>

https://play.golang.org/p/Ehr8qx1aWf

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
    &lt;Locations totalPages=&quot;1&quot; currentPage=&quot;1&quot; pageSize=&quot;25&quot;&gt;
       &lt;WirelessClientLocation macAddress=&quot;00:00:00:00:00:00&quot;&gt;
          &lt;MapInfo mapHierarchyString=&quot;Head office&amp;gt;Ground floor&amp;gt;Store&quot; floorRefId=&quot;-1122334455667789&quot;&gt;
             &lt;Image imageName=&quot;floorPlan1.png&quot; /&gt;
          &lt;/MapInfo&gt;
          &lt;MapCoordinate x=&quot;2850&quot; y=&quot;3000&quot; unit=&quot;FEET&quot; /&gt;
       &lt;/WirelessClientLocation&gt;
       &lt;WirelessClientLocation macAddress=&quot;11:11:11:11:11:11&quot;&gt;
          &lt;MapInfo mapHierarchyString=&quot;Head office&amp;gt;Ground floor&amp;gt;Store&quot; floorRefId=&quot;-1122334455667789&quot;&gt;
	        &lt;Image imageName=&quot;floorPlan1.png&quot; /&gt;
          &lt;/MapInfo&gt;
          &lt;MapCoordinate x=&quot;10.72&quot; y=&quot;76.49&quot; unit=&quot;FEET&quot; /&gt;
       &lt;/WirelessClientLocation&gt;
       &lt;WirelessClientLocation macAddress=&quot;26:cd:96:46:0b:2b&quot;&gt;
          &lt;MapInfo floorRefId=&quot;0&quot; /&gt;
          &lt;MapCoordinate x=&quot;51.52&quot; y=&quot;4.2&quot; unit=&quot;FEET&quot; /&gt;
       &lt;/WirelessClientLocation&gt;
    &lt;/Locations&gt;

To give some context; I am working on a project integrating with a vendor in which sometimes we receive the data as XML and sometimes as JSON. I wanted to build something that could unmarshal the structure for both, rather than duplicating the structure set. It has many substructures which means that its a lot more work to keep 2 structures which are almost identical except for this one attribute.

When we receive the JSON data, the field can be given as a string or a number.

I have read that you cannot unmarshal into an interface, but does anyone know of a way around this issue for my scenario?

答案1

得分: 5

检查返回的错误总是很重要。

if err := xml.Unmarshal([]byte(xmlRawData), &xmlData); err != nil {
    fmt.Println(err)
}

你得到的错误是:

cannot unmarshal into interface {}

空接口无法进行解组,因为空接口没有任何导出字段来映射 XML 键/值。

然而,有一种方法可以解决这个问题。在你的 VendorMapInfo 结构体上实现 xml.Unmarshaler 接口。

示例:更新后的代码

type VendorMapInfo struct {
    MapHierarchyString string      `xml:"mapHierarchyString,attr"`
    FloorRefID         interface{} `xml:"floorRefId,attr"`
    Image              Image       `xml:"Image"`
    FloorDimension     VendorFloorDimension
}

type Image struct {
    Name string `xml:"imageName,attr"`
}

func (mf *VendorMapInfo) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
    // Attributes
    for _, attr := range start.Attr {
        switch attr.Name.Local {
        case "mapHierarchyString":
            mf.MapHierarchyString = attr.Value
        case "floorRefId":
            mf.FloorRefID = findFloorRefIDType(attr.Value)
        }
    }

    for {
        token, err := d.Token()
        if err != nil {
            return err
        }

        switch el := token.(type) {
        case xml.StartElement:
            if el.Name.Local == "Image" {
                item := new(Image)
                if err = d.DecodeElement(item, &el); err != nil {
                    return err
                }
                mf.Image = *item
            }
        case xml.EndElement:
            if el == start.End() {
                return nil
            }
        }
    }

    return nil
}

完整代码,Playground 链接:https://play.golang.org/p/wZQOsQv0Nq

输出:

{Locations:{Space: Local:} WirelessClientLocation:[{MacAddress:00:00:00:00:00:00 MapInfo:{MapHierarchyString:Head office>Ground floor>Store FloorRefID:-1122334455667789 Image:{Name:floorPlan1.png} FloorDimension:{Length:0 Width:0 Height:0 OffsetX:0 OffsetY:0 Unit:}}} {MacAddress:11:11:11:11:11:11 MapInfo:{MapHierarchyString:Head office>Ground floor>Store FloorRefID:-1122334455667789 Image:{Name:floorPlan1.png} FloorDimension:{Length:0 Width:0 Height:0 OffsetX:0 OffsetY:0 Unit:}}} {MacAddress:26:cd:96:46:0b:2b MapInfo:{MapHierarchyString: FloorRefID:0 Image:{Name:} FloorDimension:{Length:0 Width:0 Height:0 OffsetX:0 OffsetY:0 Unit:}}}]}

  [1]: https://golang.org/pkg/encoding/xml/#Unmarshaler
英文:

Its important to check the returned error always.

if err := xml.Unmarshal([]byte(xmlRawData), &amp;xmlData); err != nil {
    fmt.Println(err)
}

The error you're getting is

cannot unmarshal into interface {}

Empty interface cannot be unmarshalled since the empty interface doesn't have any exported fields to map the xml keys/values to.

However there is way to get around. Implementing xml.Unmarshaler interface on your VendorMapInfo struct.

Example: Your updated code

type VendorMapInfo struct {
	MapHierarchyString string      `xml:&quot;mapHierarchyString,attr&quot;`
	FloorRefID         interface{} `xml:&quot;floorRefId,attr&quot;`
	Image              Image       `xml:&quot;Image&quot;`
	FloorDimension     VendorFloorDimension
}

type Image struct {
	Name string `xml:&quot;imageName,attr&quot;`
}

func (mf *VendorMapInfo) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	// Attributes
	for _, attr := range start.Attr {
		switch attr.Name.Local {
		case &quot;mapHierarchyString&quot;:
			mf.MapHierarchyString = attr.Value
		case &quot;floorRefId&quot;:
			mf.FloorRefID = findFloorRefIDType(attr.Value)
		}
	}

	for {
		token, err := d.Token()
		if err != nil {
			return err
		}

		switch el := token.(type) {
		case xml.StartElement:
			if el.Name.Local == &quot;Image&quot; {
				item := new(Image)
				if err = d.DecodeElement(item, &amp;el); err != nil {
					return err
				}
				mf.Image = *item
			}
		case xml.EndElement:
			if el == start.End() {
				return nil
			}
		}
	}

	return nil
}

Complete code, play link: https://play.golang.org/p/wZQOsQv0Nq

Output:

{Locations:{Space: Local:} WirelessClientLocation:[{MacAddress:00:00:00:00:00:00 MapInfo:{MapHierarchyString:Head office&gt;Ground floor&gt;Store FloorRefID:-1122334455667789 Image:{Name:floorPlan1.png} FloorDimension:{Length:0 Width:0 Height:0 OffsetX:0 OffsetY:0 Unit:}}} {MacAddress:11:11:11:11:11:11 MapInfo:{MapHierarchyString:Head office&gt;Ground floor&gt;Store FloorRefID:-1122334455667789 Image:{Name:floorPlan1.png} FloorDimension:{Length:0 Width:0 Height:0 OffsetX:0 OffsetY:0 Unit:}}} {MacAddress:26:cd:96:46:0b:2b MapInfo:{MapHierarchyString: FloorRefID:0 Image:{Name:} FloorDimension:{Length:0 Width:0 Height:0 OffsetX:0 OffsetY:0 Unit:}}}]}

huangapple
  • 本文由 发表于 2017年7月27日 01:08:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/45333364.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定