divan / gorilla-xmlrpc

Gorilla XML RPC implementation (Golang/Go)
BSD 3-Clause "New" or "Revised" License
69 stars 59 forks source link

Empty arrays issue: type mismatch: string != %!s(<nil>) or type mismatch: string != []interface {} #24

Closed vhanla closed 5 years ago

vhanla commented 6 years ago

Hello, I'm using this library to create a Metaweblog API server for Open Live Writer. However this has some problems with the following XML-RPC request:

<?xml version="1.0" encoding="utf-8"?>
<methodCall>
 <methodName>metaWeblog.newPost</methodName>
 <params>
  <param>
   <value>
    <string>1</string>
   </value>
  </param>
  <param>
   <value>
    <string>myusername</string>
   </value>
  </param>
  <param>
   <value>
    <string>mypass123</string>
   </value>
  </param>
  <param>
   <value>
    <struct>
     <member>
      <name>title</name>
      <value>
       <string>Post Title</string>
      </value>
     </member>
     <member>
      <name>description</name>
      <value>
       <string>Post content html escaped</string>
      </value>
     </member>
     <member>
      <name>categories</name>
      <value>
       <array>
        <data /><!---  HERE IS THE PROBLEM, AN EMPTY ARRAY --->
       </array>
      </value>
     </member>
    </struct>
   </value>
  </param>
  <param>
   <value>
    <boolean>1</boolean>
   </value>
  </param>
 </params>
</methodCall>

The problem is in the ARRAY, which sometimes OLW sends as shown above <data/> when there are no categories, but when there are, it sends as follows:

...
        <data>
         <value>
          <string>Category1</string>
         </value>
         <value>
          <string>Category2</string>
         </value>
        </data>
...

So, this last one works if I use []strings not []interfaces{} as you suggest on the readme. However, if there is no categories (<data/>) it will fail returning FAULT errors of type mismatch, since it will consider it as a single string instead of empty array of strings.

So, here is that part of my code:

type Post struct {
    DateCreated atime.Time  `xml:"dateCreated"`
    Description string      `xml:"description"`
    Title       string      `xml:"title"`
    Categories  []string    `xml:"categories"` //<---  Array of strings, []interfaces{} doesn't work
    Permalink   string      `xml:"permalink"`
    PostId      interface{} `xml:"postid"`
    UserId      string      `xml:"userid"`
    WP_SLUG     string      `xml:"wp_slug"`
    MT_Excerpt  string      `xml:"mt_excerpt"`
    Link        string      `xml"link"`
...
func (h *WebService) NewPost(r *http.Request, args *struct {
    Blogid, Username, Password string
    APost                      Post
    Publish                    bool
}, reply *struct{ Data bool }) error {
    reply.Data = args.Publish
    return nil

So, Post structure is passed in args, but it fails if no categories are included.

Using []interfaces{} for Array type is not working in both cases. []string works when the array of categories has at least one entry, but if empty, it doesn't. string works when the array "is empty", i.e <data/> is sent instead of values by Open Live Writer as shown above. So I would need 2 Post structures for both scenarios, when array is empty (considered as string) and when there is actually an array (in this case considered as []string). A function overload wouldn't be an option since go doesn't support it, and variadic is not working too. It might be OLW request issue. But I'm curious of how to deal with a MethodCall with the same MethodName for different parameters.

I'm new to Golang, so would you be kind to tell me how to solve that issue? Maybe I should modify xml2rpc function, but I have no clue of how to start in there.

Anyway. Thanks for this library, it gave me a good start. Now, I can use OLW with my simple test website written in Golang.

vhanla commented 6 years ago

Well, I added to xml2rpc.go in value2Field function the following case:

case len(value.Array) == 0:
        val = val

I suppose it is fine, I would also do a comparison in default:'s value.raw string, with the string:

<array>
   <data />
 </array>

But that would be not really fine.

I've also read the XML-RPC specification arrays section, and it seems that <data /> inside <array> tags would indeed mean an empty array.

If that workaround is correct, please feel free to close this issue.

Bischoff commented 5 years ago

Same here. I get the string

"<array><data></data></array>"

when trying to decode an empty array.

mbrookhuis commented 5 years ago

This issue is being solved with the solution mentioned by vhanla. Could you please add this to the code.

Bischoff commented 5 years ago

Right. I created the Pull Request: https://github.com/divan/gorilla-xmlrpc/pull/26 .

When merged, I guess we can close this issue.

divan commented 5 years ago

I apologize for not taking a look on the issue for that long. PR is merged now.

divan commented 5 years ago

I'm not working with XML-RPC anymore, so if anyone want join as a maintainer of this package, feel free to contact me.

Bischoff commented 5 years ago

If no one else steps up, maybe me, but I would need, for each of the 5 currently open issues and each of the 7 currently open pull requests, your opinion on the matter and the action that should be taken. Otherwise I fear the transition would be too difficult, I don't want to read all the discussion, and need to test.

If you agree with it, it can be done here, or on in private mail.

Bischoff commented 5 years ago

(@divan, I think this issue can be closed now? I can't)

divan commented 5 years ago

@Bischoff feel free to contact me via email (ivan.daniluk at gmail)

justinscorringe commented 5 years ago

I am getting the same error as the title. I am trying to parse a xml response which is an array with an int and a string inside (params/param/value/array/data/value/int or string); currently I am using an empty interface,

(reply struct { AReply []interface{} }, err error)

Could anyone guide me on how to parse this response? I've looked at every example and can seem to find any parallels. Thank you very much!

Bischoff commented 5 years ago

Hi Justins,

Admitting this is indeed same error, you have to apply the patch mentioned above:

Well, I added to xml2rpc.go in value2Field function the following case:

case len(value.Array) == 0:
      val = val

This patch has now been merged, so if you reinstall now, it should work out of the box.

Please tell me if it solved your problem.

justinscorringe commented 5 years ago

Hi Bischoff Thank you for the response! I appear to have a very similar error but my apologies! I did not notice it is was not identical! After integrating that case into xml2rpc.go, I have this result

Invalid Method Parameters: fields type mismatch: int != %!s()

The method response I am trying to parse is an array of ints and strings (in either order), not just strings as the case above mentions. I would suppose I need to define my array explicitly maybe, but I am unsure how to go about this for a dynamic array.

Bischoff commented 5 years ago

Justin,

I think you must define explicitly AReply. For an array of fixed order ints and strings, it would work like this:

type AReply struct {
  SomeNumber int
  SomeText string
}

and then you would use your array of Areplys.

I'm not sure you can intermingle integers and strings in any order. I think gorilla-xmlrpc has to know about the structure of your data.

Maybe @divan knows. But I'm pretty sure it will not work because value definition in file xml2rpc.go does not include interface as one of the types it knows. You would need to add support for that type, and it's no easy thing.

You could try one of these workarounds:

Eric

justinscorringe commented 5 years ago

Hi Bischoff I can setup explicit definitions for now. However I am lost on: reply struct{some int, some string, some int} expects the values not in an array, reply struct{ AReply struct{ some int, some string, some int} expects an xml struct.

I understood []interface{} as the only way to read the xml-array layout? Thank you

`

` `` `` ` ` ` 1 ` ` Some string ` ` 0 ` `` `` ` ` `

`

Bischoff commented 5 years ago

Hi again,

you are alternating ints and strings in random order, I don't think you can do it, because you would need an array of "varying" types (interfaces in Go parlance) and I don't think gorilla-xmlrpc can handle that.

You can have an array of structs. If you can send this

<array>
   <data>
     <struct>
        <value> <int> 1 </int> </value>
        <value> <string> Some string </string> </value>
     </struct>
     <struct>
        <value> <int> 0 </int> </value>
        <value> <string> </string> </value>
     </struct>
   </data>
</array>

then you're good.

HTH,

justinscorringe commented 5 years ago

Ah. The methodresponse I've shown can't be modified unfortunately, It is external and I am only using this package to receive and parse it. I do know however that the exact format would be int, string,int in that instance. Thank you for your help, I will have to start looking at alternatives.

Bischoff commented 5 years ago

Sure.