#1 2015-10-05 10:49:14

squirrel
Member
Registered: 2015-08-13
Posts: 146

Dynamic named array as input

What would be the correct format to send a named dynamic array to an interface function?

I declared the following as a test:

  TFilterType = record
    name: string;
    value: string;
    comparetype: string;
  end;
  TFilterList = array of TFilterType;

...

function TStaff.FilterStaff(const filters: TFilterList): TFilterList;
begin
  Log('Received filters: '+IntToStr(length(filters)) );

  setlength(result, 2);
  result[0].name := 'name1';
  result[0].value := 'value1';
  result[0].comparetype := '>';
  result[1].name := 'name2';
  result[1].value := 'value2';
  result[1].comparetype := '=';
end;

The function correctly returns

{"result":[[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype"
:"="}]]}

But when sending

var dataString = {"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]};

...

http://localhost:8080/root/Staff/FilterStaff?filters%5B0%5D%5Bname%5D=name1&filters%5B0%5D%5Bvalue%5D=value1&filters%5B0%5D%5Bcomparetype%5D=%3E&filters%5B1%5D%5Bname%5D=name2&filters%5B1%5D%5Bvalue%5D=value2&filters%5B1%5D%5Bcomparetype%5D=%3D&session_signature=3eaeb14d00379c791c58bcc3

an empty array is received.

Offline

#2 2015-10-05 11:05:56

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,231
Website

Re: Dynamic named array as input

Do not inline the parameters, but use a POST with a body storing the JSON input as expected.

Offline

#3 2015-10-05 11:12:39

squirrel
Member
Registered: 2015-08-13
Posts: 146

Re: Dynamic named array as input

Unfortunately posting the values causes a 406 Not Acceptable error.

var dataString = {"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]};
jQuery.ajax({
		type: "POST",
		dataType: "json",
		url: MAIN_URL + '/Staff/FilterStaff',
		data: dataString,
		timeout: 2000
	});

Offline

#4 2015-10-05 11:36:22

edwinsn
Member
Registered: 2010-07-02
Posts: 1,215

Re: Dynamic named array as input

I guess a workaround would be making a wrapper function around TStaff.FilterStaff() which accepts json string instead, But definitely it's perfect if @ab can fix it.


Delphi XE4 Pro on Windows 7 64bit.
Lazarus trunk built with fpcupdelux on Windows with cross-compile for Linux 64bit.

Offline

#5 2015-10-05 12:04:59

squirrel
Member
Registered: 2015-08-13
Posts: 146

Re: Dynamic named array as input

Thanks edwin.  I can do a wrapper function and then just use that when calling via ajax and use the real function for delphi clients.  However, I suspect this will add a lot of overhead and will need to be done throughout.

Any suggestions what would be the most efficient way of converting the json string to the array?

Offline

#6 2015-10-05 12:06:33

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,231
Website

Re: Dynamic named array as input

Check in TServiceMethodExecute.ExecuteJson where it fails to parse input content.
Are you sure the body is really some valid JSON?
I'm afraid you are sending some string over the wire.

Also try to define the record as "packed record", and try text-based serialization.

Offline

#7 2015-10-05 12:21:20

squirrel
Member
Registered: 2015-08-13
Posts: 146

Re: Dynamic named array as input

Problem found!  packed record did not make a difference, but ab put me on the right track.  For future ajax users, send the json as string, not object.  To correct the example above, I changed

var dataString = {"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]};

to

var dataString = '{"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]}';

However this messes up appending the session signature when using GET.  So Post is forced.

In TServiceMethodExecute.ExecuteJson's case statement, Par^ detects the first character as f and not as { when parsing.  Adding the quotes resolved it.

Last edited by squirrel (2015-10-05 12:27:39)

Offline

#8 2015-10-05 12:42:21

squirrel
Member
Registered: 2015-08-13
Posts: 146

Re: Dynamic named array as input

Looks like the parsing is very fragile.  Adding more parameters to the function highlights that some exact format is required, even though it succeeds in browser parsing.

Changing the function to:

function TStaff.FilterStaff(const filters: TFilterList; Limit: integer): TFilterList;

and the datastring to

var dataString = '{"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}], "Limit":"100"}';

gives the 406 same issue again, but the json validates successfully on sites such as jsonschema.

Using this format, works, provided that the correct type (integer vs string) is passed.  Note that this json doesnt validate on sites such as jsonschema.net:

var dataString = '{"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]}, {"Limit":"100"}';

This works for integer Limit parameter:

var dataString = '{"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}]}, {"Limit":100}';

Last edited by squirrel (2015-10-05 13:28:31)

Offline

#9 2015-10-05 13:37:14

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,231
Website

Re: Dynamic named array as input

Please try to see in TServiceMethodExecute.ExecuteJson why

var dataString = '{"filters":[{"name":"name1","value":"value1","comparetype":">"},{"name":"name2","value":"value2","comparetype":"="}], "Limit":"100"}';

does not work.

Offline

#10 2015-10-05 13:50:38

squirrel
Member
Registered: 2015-08-13
Posts: 146

Re: Dynamic named array as input

I suspect the browser had a cached version of the js file where the value 100 was "100" ie as a string.  It is working now after clearing caches.  Thanks ab!

Offline

#11 2015-10-05 14:28:36

warleyalex
Member
From: Sete Lagoas-MG, Brasil
Registered: 2013-01-20
Posts: 250

Re: Dynamic named array as input

If you want to send the data as JSON, you have to encode it first:

var dataString = {
    "result" : [[{
                "name" : "name1",
                "value" : "value1",
                "comparetype" : ">"
            }, {
                "name" : "name1",
                "value" : "value2",
                "comparetype" : "="
            }
        ]]
};

jQuery.ajax({
    type : "POST",
    dataType : "json",
    contentType : "application/json; charset=utf-8",
    url : 'http://localhost:8080/root/MyService.FilterStaff',
    data : JSON.stringify(dataString)
});

Offline

#12 2015-10-05 14:30:58

ab
Administrator
From: France
Registered: 2010-06-21
Posts: 14,231
Website

Re: Dynamic named array as input

I've tried to reproduce the issue, but found none.

See http://synopse.info/fossil/info/0f3246860b

Offline

Board footer

Powered by FluxBB