JSON issues

I'm experimenting with the JSON serializer / deserializer in combination with DataSnap and I think I am overlooking something. Consider the following:




  [Entity, Automapping]
  TCountry = class
  private
    FID: Integer;
    FName: string;
  public
    property ID: Integer read FID write FID;
    property Name: string read FName write FName;
  end;

  [Entity, Automapping]
  TPerson = class
  private
    FID: Integer;
    FName: string;
    FCountry: TCountry;
  public
    property ID: Integer read FID write FID;
    property Name: string read FName write FName;
    property Country: TCountry read FCountry write FCountry;
  end;


I have country 1: NL and 2: BE.

A person named John from Belgium is then serialized to JSON which will result in:

{"result":[{"$type":"uModel.TPerson","$id":1,"FID":1,"FName":"John","FCountry":{"$type":"uModel.TCountry","$id":2,"FID":2,"FName":"BE"}}]}



Now I want to chane the country for John to BE. I have another function that returns the list of countries:

{"result":[[{"$type":"uModel.TCountry","$id":1,"FID":1,"FName":"NL"},{"$type":"uModel.TCountry","$id":2,"FID":2,"FName":"BE"}]]}

'

So now I change the country for John from the BE country to NL and post this to my Datasnap REST server:

{"$type":"uModel.TPerson","$id":1,"FID":1,"FName":"John","FCountry":{"$type":"uModel.TCountry","$id":1,"FID":1,"FName":"NL"}}





Now when I post this I guess the first thing to do is deserialize this back to a TPerson. However, the deserializer gives an exception: No duplicates allowed. I think this is because some internal meganism relies on the $id value and that is indeed duplicate now.



So my question:



With a Datasnap rest-client and the above example, what is the suggested method to change, in a javascript REST-client, the country for this person?



I hope you can help me out, I am realy looking forward to use aurelius and buy a copy once I get this to work!

Hello, this should work. The id of TCountry should not be in conflict with the id of TPerson, Can you post more parts of the code, how you change the country, how do you deserialize, so that we can have a better idea?

This is how you can reproduce it:



Create a new Datasnap REST server with the wizard, include the sample methods (reversestring, echostring). Replace ServerMethodsUnit1 with the code below.



Start the server and go to the ServerFunctionInvoker on http://localhost:8080/ServerFunctionInvoker?

Execute Person and this is the result:

{"$type":"ServerMethodsUnit1.TPerson","$id":1,"FID":1,"FName":"John","FCountry":{"$type":"ServerMethodsUnit1.TCountry","$id":2,"FID":1,"FName":"NL"}}



(as a test copy-paste this in updatePerson and execute: no problem!)



Execute Countries and this is the result:

[{"$type":"ServerMethodsUnit1.TCountry","$id":1,"FID":2,"FName":"BE"},{"$type":"ServerMethodsUnit1.TCountry","$id":2,"FID":1,"FName":"NL"}]



That is all good.



Replace the country in the Person JSON with the BE details so that it becomes this:

{"$type":"ServerMethodsUnit1.TPerson","$id":1,"FID":1,"FName":"John","FCountry":{"$type":"ServerMethodsUnit1.TCountry","$id":1,"FID":2,"FName":"BE"}}



Execute -> exception: no duplicates allowed.



Notice that both TPerson and TCountry have $id = 1. Change one of them to $id = 2 and EXECUTE -> No problem, verify the change by executing Person again.



So I guess it has something to do with the duplicate $id?





Code for ServerMethodsUnit1:

-----------------------------------------------------



unit ServerMethodsUnit1;



interface



uses System.SysUtils, System.Classes, Datasnap.DSServer, Datasnap.DSAuth, DBXJSON, Aurelius.Mapping.Attributes;



type

{$METHODINFO ON}

TServerMethods1 = class(TComponent)

public

    function Person: TJSONValue;

    procedure updatePerson(Value: TJSONValue);

    function Countries: TJSONValue;

end;

{$METHODINFO OFF}



[Entity, Automapping]

TCountry = class

private

    FID: Integer;

    FName: string;

public

    property ID: Integer read FID write FID;

    property Name: string read FName write FName;

end;



[Entity, Automapping]

TPerson = class

private

    FID: Integer;

    FName: string;

    FCountry: TCountry;

public

    property ID: Integer read FID write FID;

    property Name: string read FName write FName;

    property Country: TCountry read FCountry write FCountry;

end;



implementation



uses System.StrUtils, Generics.Collections, Aurelius.Json.DataSnap;



var

FPerson: TPerson;

FCountryNL: TCountry;

FCountryBE: TCountry;



function TServerMethods1.Countries: TJSONValue;

var

CL: TObjectList<TCountry>;

FSerializer: TDataSnapJsonSerializer;

begin

CL := TObjectList<TCountry>.Create(False);

try

    CL.Add(FCountryBE);

    CL.Add(FCountryNL);

    try

      FSerializer := TDataSnapJsonSerializer.Create;

       Result := FSerializer.ToJson(CL);

    finally

      FSerializer.Free;

    end;

finally

    CL.Free;

end;

end;



function TServerMethods1.Person: TJSONValue;

var

FSerializer: TDataSnapJsonSerializer;

begin

FSerializer := TDataSnapJsonSerializer.Create;

try

    Result := FSerializer.ToJson(FPerson);

finally

    FSerializer.Free;

end;

end;



procedure TServerMethods1.updatePerson(Value: TJSONValue);

var

FDeSerializer: TDataSnapJsonDeserializer;

begin

FDeSerializer := TDataSnapJsonDeserializer.Create;

try

    FPerson := FDeSerializer.FromJson<TPerson>(Value);

finally

    FDeSerializer.Free;

end;

end;



initialization

FCountryNL := TCountry.Create;

FCountryNL.ID := 1;

FCountryNL.Name := 'NL';



FCountryBE := TCountry.Create;

FCountryBE.ID := 2;

FCountryBE.Name := 'BE';



FPerson := TPerson.Create;

FPerson.ID := 1;

FPerson.Name := 'John';

FPerson.Country := FCountryNL;



end.



testwerewr

Ah, ok. Yes, the $id is just an internal name for the json serializer/deserializer. It MUST be unique among all objects in a single JSON representation.

But how can I make a change to a JSON object then? Right now the JSON serialization is sort of 'read-only' since it is not possible to substitue one sub-object for another if they have the same $id. The person-country relation kind of relation is very common in business applications.

I believe you are replacing the json string directly? If yes, that's the problem. If you just use the json serializer, it will automatically set the id values for you

I dont'understand your answer. I have a Javascript client that receives the json from the serializer.

First GET-request: the list of countries.

Second GET-request: the person (incl the country that belongs to this person).



The person json is changed (the country is substituted) and then posted back to Datasnap where it is passed to the deserializer, which fails.



Can you show an example or explain to me how this can or should be be implemented? What I want (I'll just relate it to this example): have a webpage where a person is loaded, the person.country is changed and then the person is saved.

Understood. Well in this case you would have to manually replace the value of $id property.