I am trying to setup services to use XML-RPC and allow an external Web Service (ASP.NET) to create a user using the user.save method. The guy who is writting the ASP script on the client side has been able to successfully connect to services and login using system.connect and user.login methods but nothing we've tried works in user.save. I've tried using the test page at admin/build/services/browse/user.save and when I input JSON
{"name":"tester","pass":"Tester123","mail":"tester@domain.com","roles":{"2":"authenticated user"}}
I get a 500 server error. The PHP logs say:
PHP Fatal error: Cannot use object of type stdClass as array in C:\inetpub\wwwroot\sites\all\modules\services\services\user_service\user_service.inc on line 187

This is Drupal 6.15 on IIS with PHP5.

CommentFileSizeAuthor
#15 json-object.patch1.24 KBjhl.verona
#8 679494.patch802 bytesgdd

Comments

thijsvdanker’s picture

I could use such an example string as well!

marcingy’s picture

Project: Services » JSON server
Version: 6.x-2.x-dev » 6.x-1.x-dev
abritez’s picture

I am having a similar issue with node.save, or pretty much any nested array I try to send to JSON_server

Liviooo’s picture

Sames issues here ... how to fix this ?

cowgp’s picture

I'm struggling with it as well... no matter what I seem to pass in to user.save it gives a fatal PHP error.

venkyyy’s picture

Facing the same problem user.save is giving fatal error. Please help

skyredwang’s picture

Project: JSON server » Services
Version: 6.x-1.x-dev » 6.x-2.x-dev
Status: Active » Postponed (maintainer needs more info)

move it to the Services issue queue , and need more information to replicate the error

gdd’s picture

StatusFileSize
new802 bytes

I believe the attached patch will solve the problem, at least in the browser. If you still have problems after this then it is a JSON Server issue. I was able to save a user successfully in the browser using this string

{"name":"tester","pass":"Tester123","mail":"tester@domain.com","roles":{"2":"authenticated user"}}

using the attached patch.

gdd’s picture

Status: Postponed (maintainer needs more info) » Needs review
drm’s picture

I must be missing something. When I enter the string as specified in #8 in the form at admin/build/services/browse/user.save, I get a blank page. No errors, no log entries. And no new user.

I can use the methods that just require a uid, like delete or get. But nothing happens with user.save, and it's the only one I need.

And if I do get that working, I don't see what path a remote user would use with an rpc call to invoke it.

Thanks for any help.

gdd’s picture

Make sure you have 'JSON' selected next to the field when using the string above. I'm not sure what would cause a blank page, I've never seen that before.

Your RPC calls will go to /services/xmlrpc

drm’s picture

Yes, JSON is the default, and I tried using php with that format just in case that made a difference.

One other possibility. I was looking at the code and see you use arg(). My client's test site is not in their domain root. It is in http://www.exmple.com/drupal/sitename. Might this be confusing the arguments?

Any shortcuts to debug why this isn't getting through?

gdd’s picture

FYI I've committed the patch in #8

jhl.verona’s picture

Erm, I don't think that's the problem. Using the Web UI, and JSON format, the { "key: "value" } syntax is converted by json_decode into an object and not an associated array (see the first example).

It is enough to convert it back into an array. Right now I'm struggling with User Protect integration, but the following code:

  // json_decode returns an object
  if (is_object($account)) {
    $account = (array)$account; 
  }

as the first lines of the functions user_service_save and user_service_save_access seems to have cured the problem for me.

As soon as I've finished the User Protect patch, I'll prepare another for this problem...

Best regards,

John

jhl.verona’s picture

StatusFileSize
new1.24 KB

Here's the patch.

gdd’s picture

Status: Needs review » Fixed

The reason that it was being converted by json_decode into an object and not an array, is because the service definition had specified it as a 'struct' instead of an array. In services_admin_browse.inc on line 223:

$return[$c] = json_decode($value, $arg['type'] === 'array');

Because the service's argument was incorrectly specified as 'struct', services was returning an object. Changing it to array fixes this issue.

Note the original issue was only about fixing an error in the services browser, not about the service itself which has been working well for some time.

jhl.verona’s picture

First of all, just let me say that I'm very grateful for the work that you have done on this module, it has saved me a great deal of time. My small attempts with these patches is to "give something back" in way of my appreciation. As a gift, whether they are used or not is entirely up to you (I rarely wear the Christmas ties I always seem to receive).

Nonetheless, I'm sorry to be pig headed about this, but I'm now more confused than ever. I've updated my CVS version so that I now have the commited patch from #8 above.
Perhaps it's because there are differences between a 'struct' and 'array' if we're talking about XML-RPC, JSON or PHP. Please bare with me here.

As I see things:

Term XML-RPC JSON PHP
struct (name/value pairs) struct object object or associated array
array (heterogeneous list) array array array

I've seen (and understood) the code in services_admin_browse.inc on line 223. Again the example in http://php.net/manual/en/function.json-decode.php makes this clear. I have no qualms here.

I just don't understand why you've changed the definition of the parameter from 'struct' to 'array'. I start thinking (XML-RPC or JSON) heterogeneous list, er, for a user object?

Changing this definition has also changed the signature of the method to the outside world (I'm egoistically thinking of XML-RPC here, because that's what I need).

So if I ask the Web UI via admin/build/services/browse/system.getServices, it now tells me (simplified):

[method] => user.save
[args] => Array
    (
        [0] => Array
            (
                [name] => account
                [type] => array
                [description] => A user object.
                [optional] => 
            )
    )
[return] => int
[help] => Save user details.

And it is also saying (again simplified) the same thing via XML-RPC:

<methodResponse>
  <params><param>
    <value> 
      <array><data>
        <value><struct>
          <member><name>method</name><value><string>user.save</string></value></member>
          <member><name>args</name><value>
            <array><data>
              <value><struct>
                <member><name>name</name><value><string>account</string></value></member>
                <member><name>type</name><value><string>array</string></value></member>
                <member><name>description</name><value><string>A user object.</string></value></member>
                <member><name>optional</name><value><boolean>0</boolean></value></member>
              </struct></value>
            </data></array>
          </value></member>
          <member><name>return</name><value><string>int</string></value></member>
          <member><name>help</name><value><string>Save user details.</string></value></member>
        </struct></value>
      </data></array>
    </value>
  </param></params>
</methodResponse>

Don't yer just love XML? Though it's pretty difficult to understand quite what is being said, it does say "if you want to save a user, then you'd better give me an array". But an XML-RPC array is different from an XML-RPC struct.

I'm being deliberately pedantic here, because that is exactly how I imagine the third party charged with writing the client side code (probably in C#) to my Drupal based intranet will react.

Curiously my own PHP client script (which sends a 'struct') still works. I'd expected it to return an error - "expecting 'array' but saw 'struct'" or something. OK, so I can always tell them "ignore it, just send a 'struct'".

But other things just don't seem right to me. Going to the Web UI for save.user at admin/build/services/browse/user.save the first thing I notice is that the default format for the account is now 'comma delimited' - good luck with that one. (Though it's perfectly correct for an array, y'know a heterogeneous list). I checked by the way, services_admin_browse.inc, line 215.

And yes, the JSON works, but probably for the same reasons that the XML-RPC struct works. But I have to send a JSON object (pronounced 'struct' of course), not a JSON array which would have [ 1, "a", true ] syntax.

My own feeling was that the XML-RPC struct should become a PHP object, but you have to take care of a much wider scope than mine, and perhaps the PHP XML-RPC code is in core, and therefore more or less untouchable. When I looked at your PHP code I noticed that the account is tested using array syntax, not object syntax, so it just seemed easier to me to make sure the account really was an array.

In the words of Forrest Gump - "and that's all I have to say about that".

Best regards,

John

gdd’s picture

Hey,

We do appreciate the recent spate of patches and the last thing I want to do is stomp out the dreams of a potential contributor :)

You are correct that an XMLRPC struct should become a PHP object, and an XMLRPC array should become a PHP array.

The code in the user save service always did work on an array. The service definition, which listed it as a struct, was a bug. This is because Drupal's internal user_save() function (http://api.drupal.org/api/function/user_save/6) expects an array of values it uses to modify the user object. This is in itself pretty confusing, but we try as much as possible to do as core does. So while we did change the method signature, the fact is that in theory if you were passing in an object before, it was going to fail anyways because the code in the service expects an array. I am really curious why that worked in fact. I really can't explain it to be honest. I have really only had experience calling these services from XMLRPC in PHP, which seems to be extremely forgiving in terms of how it treats parameters, so perhaps that is it. It is possible that some more strict clients may complain about this method signature but again, the other way it break too.

I'm not sure how the default input format in the browser is picked, I should have a look at that. Honestly any extended text in there is kind of iffy no matter what the format.

The XMLRPC server code we are leeching off of is in fact in core so you are right that if a modification needed to be made there, then it would be a different issue queue anyways.

So I think we're both saying the same thing anyways :) Hope you're note getting discouraged. Thanks for the help.

jhl.verona’s picture

Not discouraged, just confused. Anything I haven't quoted from your previous comment - I agree with entriely ;-)

We do appreciate the recent spate of patches and the last thing I want to do is stomp out the dreams of a potential contributor :)

No, you're just making me think harder. And I may be a little out of practice...

We're not (quite) saying the same thing. To simplify, I'm talking about XML-RPC (only), and not how it is implemented. Try to imagine that all I know is a little XML-RPC, and I have no idea how the service/server is implemented (actually not that far from the truth).

I use node.get to retrieve information about some node. What does the service say I'll get back? A struct. What do I get back? A struct.
Next, I want to update a node. I use node.save What does it want as an argument? A struct.
Hey, wait a minute, I can just modify what I got and then send it back. Nice and simple.

I use user.get to retrieve information about some user. What does the service say I'll get back? A struct. What do I get back? A struct.
Next, I want to update a user. I use user.save What does it want as an argument? An array. Shome mishtake, shurely? Ed

The service definition, which listed it as a struct, was a bug.

In that case, it's a bug in node.save too? (Please don't say yes - I'm trying to prove the opposite ;-).
That service (node_service_save, line 112) also uses a drupal_execute, but it just forces the incoming data into an array - doesn't even bother to test.

As far as the internal implementation is concerned, "c'est la vie". Whether the data came from XML-RPC or JSON, or whatever, you're going to have to match what you get with what the function you call wants. To me, node_service_save is the correct solution.

I'm not sure how the default input format in the browser is picked, I should have a look at that.

Actually it was you that pointed it out in #16. I was using that in an attempt to 'prove' that struct was right, and that array is wrong. It uses the argument value to provide the input types (services_admin_browse_test, line 143).

Excellent idea, by the way, the Web UI makes things pretty easy to test - if you know a little JSON.

Am I winning?

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

arski’s picture

Status: Closed (fixed) » Active

Hey guys,

I kind of bumped into this same issue today.. I was testing the user.save method by Browsing and I got exactly this issue with the latest stable release.. Then I updated to -dev and it went away, so from the browser I could add users perfectly - great.

Now when I tried to do the same using JS, it appears that again, the $account value that the user_save and user_save_access functions are getting is an object, so I applied the patch from #15 which makes it work.

Just wondering if you are aware of this? From what I get in the comment #16, patch #15 was deemed unnecessary.. however, some things still don't seem to be working.

Thanks a lot in advance!

sliddell’s picture

Issue tags: +services, +user.save, +json

I'm currently trying to create a new user using this POST:

{"sessid":"voqfog2u17puytjj9e7qb8t44","method":"user.save","account":{"name":"Test1","pass":"test1","mail":"myemailadd@hotmail.com","roles":{"2":"authenticated user"}}}

Unfortunately I'm getting the error:

{"#error":true,"#data":"The e-mail address C<\/em> is not valid."}

If I take out the mail property I get the same error - so i'm guessing i'm putting this in the wrong way .. any suggestions?

marcingy’s picture

Status: Active » Closed (fixed)
newnewuser’s picture

I did apply the patch. But yet, user.save doesn't take the "roles" argument. Can anyone suggest how to trace the issue. Thx

newnewuser’s picture

Status: Closed (fixed) » Active
marcingy’s picture

Status: Active » Closed (fixed)

Please do not reopen old tickets. Roles is not an argument it is part of the array structure being passed in.