use-go / onvif

full and enhanced onvif protocol stack in golang.
MIT License
384 stars 180 forks source link

unmarshal support for onvif soap response #2

Open ilia373 opened 4 years ago

ilia373 commented 4 years ago

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?

YoshieraHuang commented 4 years ago

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

crazybber commented 3 years ago

a little busy ,but PRs are always welcomed.😊

Miracle-doctor commented 2 years ago

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

AkshayPS12 commented 2 years ago

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

unm4sk1g commented 2 years ago

Any progress on this?

AkshayPS12 commented 2 years ago

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)
    }
unm4sk1g commented 2 years ago

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 👍

cedricve commented 2 years ago

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>
AkshayPS12 commented 2 years ago

@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

Pawan-ky commented 11 months ago

Hi, do we have concrete solution on this issue (other than the solution mentioned above) or anybody working on this and planning to release in near future.