onvif icon indicating copy to clipboard operation
onvif copied to clipboard

unmarshal support for onvif soap response

Open ilia373 opened this issue 4 years ago • 10 comments

currently there is soap xml marshaling tags only for the request structs. for response structs there are no xml unmarshal capabilities. planning to add that support? there is any technical limitation to have that implemented or its only a matter of mapping each struct?

ilia373 avatar Jun 22 '20 14:06 ilia373

I agree. large xml file is too tedious to handle.

YoshieraHuang avatar Jul 08 '20 07:07 YoshieraHuang

a little busy ,but PRs are always welcomed.😊

crazybber avatar Aug 17 '20 10:08 crazybber

Hi! I used this code for unmarshalling

type ProfileResponse struct { XMLName xml.Name Profiles []onvif.Profile } type Body struct { XMLName xml.Name GetProfilesResponse ProfileResponse //xml:"GetProfilesResponse" } type MyGetProfiles struct { XMLName xml.Name Body Body }

	profiles := &MyGetProfiles{}
	err = xml.Unmarshal([]byte(resp), profiles)

And for getting profiles:

profiles.Body.GetProfilesResponse.Profiles

Miracle-doctor avatar Jul 28 '21 07:07 Miracle-doctor

if anybody has figured out the unmarshalling of XML responses please let me know , or if someone has started working , we can collaborate...

AkshayPS12 avatar Aug 24 '21 11:08 AkshayPS12

Any progress on this?

unm4sk1g avatar Aug 26 '21 11:08 unm4sk1g

Any progress on this?

I have done this in a kind of hacky way : This function will query the node from the xml body you want to extract the info from For example , you do getStreamUri call from dev.CallMethod() and you get a xml resp which contains GetStreamUriResponse

func getXMLNode(xmlBody string, nodeName string) (*xml.Decoder, *xml.StartElement, string) {

	xmlBytes := bytes.NewBufferString(xmlBody)
	decodedXML := xml.NewDecoder(xmlBytes)

	for {
		token, err := decodedXML.Token()
		if err != nil {
			break
		}
		switch et := token.(type) {
		case xml.StartElement:
			if et.Name.Local == nodeName {
				return decodedXML, &et, ""
			}
		}
	}
	return nil, nil, "error in NodeName"
}

and then you just unmarshal into the Response struct

       
   var mGetUsersResp device.GetUsersResponse
	bs, _ := ioutil.ReadAll(res.Body)
	stringBody := string(bs)

   decodedXML, et, errorFunc := getXMLNode(stringBody, "GetStreamUriResponse")
	if errorFunc != "" {
		log.Printf("%s", errorFunc)
	}
	if err := decodedXML.DecodeElement(&mStreamUriResp, et); err != nil {
		panic(err)
	}

AkshayPS12 avatar Aug 31 '21 09:08 AkshayPS12

Any progress on this?

I have done this in a kind of hacky way : This function will query the node from the xml body you want to extract the info from For example , you do getStreamUri call from dev.CallMethod() and you get a xml resp which contains GetStreamUriResponse

func getXMLNode(xmlBody string, nodeName string) (*xml.Decoder, *xml.StartElement, string) {

	xmlBytes := bytes.NewBufferString(xmlBody)
	decodedXML := xml.NewDecoder(xmlBytes)

	for {
		token, err := decodedXML.Token()
		if err != nil {
			break
		}
		switch et := token.(type) {
		case xml.StartElement:
			if et.Name.Local == nodeName {
				return decodedXML, &et, ""
			}
		}
	}
	return nil, nil, "error in NodeName"
}

and then you just unmarshal into the Response struct

       
   var mGetUsersResp device.GetUsersResponse
   bs, _ := ioutil.ReadAll(res.Body)
   stringBody := string(bs)

   decodedXML, et, errorFunc := getXMLNode(stringBody, "GetStreamUriResponse")
   if errorFunc != "" {
   	log.Printf("%s", errorFunc)
   }
   if err := decodedXML.DecodeElement(&mStreamUriResp, et); err != nil {
   	panic(err)
   }

Works flawlessly, thank you 👍

unm4sk1g avatar Sep 01 '21 13:09 unm4sk1g

thanks this does work for string, but integers are set to 0, even if there is a valid response in the body

{{VideoEncoderToken_2  0}  {0 0} 0 {0 0 0} {0 } {0 } {{  } 0 0 false} }

And the response

<tt:Name>VideoEncoder_2</tt:Name>
<tt:UseCount>1</tt:UseCount>
<tt:Encoding>H264</tt:Encoding>
<tt:Resolution><tt:Width>640</tt:Width>
<tt:Height>360</tt:Height>
</tt:Resolution>

cedricve avatar Jan 18 '22 21:01 cedricve

@cedricve make sure your struct is correct for which you are unmarshalling. You will need to understand the types. My VideoEncoderConfiguration Struct looks like this :

type VideoEncoderConfiguration struct {
	*ConfigurationEntity `json:"ConfigurationEntity"`
	Encoding             *string      `xml:"Encoding" json:",omitempty"`
	Resolution           *Resolution  `xml:"Resolution" json:",omitempty"`
	Quality              *float64     `xml:"Quality" json:",omitempty"`
	RateControl          *RateControl `xml:"RateControl" json:",omitempty"`
	MPEG4                *MPEG4       `xml:"MPEG4" json:",omitempty"`
	H264                 *H264        `xml:"H264" json:",omitempty"`
	Multicast            *Multicast   `xml:"Multicast" json:",omitempty"`
	SessionTimeout       *string      `xml:"SessionTimeout" json:",omitempty"`
}

also fields like Resolution point to another struct which is like this:

type Resolution struct {
	Width  *xsd.Int `xml:"Width"`
	Height *xsd.Int `xml:"Height"`
}

xsd Int is basically type Int int32

these structs are autogenerated from ONVIF's xsd

AkshayPS12 avatar Jan 22 '22 08:01 AkshayPS12