0
votes

I have a Django backend model with REST Services which create JSON. The JSON I get (part of it) looks like follows:

[
  {
    "id": 1,
    "username": "T1",
    "tel": "",
    "created_on": "2018-09-03T16:29:20.903315Z",
    "last_login": null,
    "hasContacts": [

    ],
    "myChanges": [

    ],
    "helpsOnChanges": [

    ],
    "shouldHelpOnChanges": [
      1
    ],
    "welldone_set": [
      {
        "id": 1,
        "created_on": "2018-09-03T16:29:20.925327Z",
        "comment": "You rock1",
        "change": 1,
        "helper": 1
      }
    ]
  },
  {
    "id": 2,
    "username": "T2",
    "tel": "",
    "created_on": "2018-09-03T16:29:20.903315Z",
    "last_login": null,
    "hasContacts": [

    ],
    "myChanges": [
      {
        "id": 1,
        "name": "Stop smoking",
        "created_on": "2018-09-03T16:29:20.923315Z",
        "change": "I want to stop smoking",
        "finished_on": null,
        "success": false,
        "helpee": 2,
        "helper": [

        ],
        "requestedHelpers": [
          1
        ],
        "welldone_set": [
          {
            "id": 1,
            "created_on": "2018-09-03T16:29:20.925327Z",
            "comment": "You rock1",
            "change": 1,
            "helper": 1
          }
        ]
      }
    ],
    "helpsOnChanges": [

    ],
    "shouldHelpOnChanges": [

    ],
    "welldone_set": [

    ]
  }
]

The explicit error of the JSONConvert.Deserialize call now is:

2018-09-02T18:57:13.145 Error Error deserializing App.ViewModels.Change. Error converting value 1 to type 'App.ViewModels.User'. Path '[0].myChanges[0].helpee', line 1, position 275.

This refers to the "helpee" attribute, which is of type user. The Django framework only puts the id of the user in this list, which makes sense as the user was already transmitted in this JSON prior to the result above.

So why cant Newtonsoft.JSON not resolve this? Cant it associate this id with the actual User instance of the same id?

Thank you in advance! Wolf

Edit : For clarification the serializer of my Django Backend:

    class ChangeSerializer(serializers.ModelSerializer):
        welldone_set = WellDoneSerializer(many=True)
        class Meta:
            model = Change
            fields = ('id',
                      'name', 
                      'created_on', 
                      'change', 
                      'finished_on', 
                      'success', 
                      'helpee', 
                      'helper', 
                      'requestedHelpers', 
                      'welldone_set') 
ChangeSerializer.helpee = UserSerializer();
ChangeSerializer.helper = UserSerializer(many=True);
ChangeSerializer.requestedHelpers = UserSerializer(many=True);

As you can see, helpee is actually an object and not just an id

Edit 2: Updated the json to be complete

Edit 3: My deserializer method. The exception gets thrown in the deserializer line.

public static async Task<List<T>> getDataListFromService<T>(string queryString)
        {
            Debug.WriteLine("###Building HttpClient for " + queryString);
            HttpClient client = new HttpClient();
            Debug.WriteLine("###Building GetAsync...");
            HttpResponseMessage response = await client.GetAsync(queryString);
            Debug.WriteLine("### HTTP Get Response: " + response.ReasonPhrase);
            List<T> data = null;
            if (response.IsSuccessStatusCode)
            {
                string json = response.Content.ReadAsStringAsync().Result;
                Debug.WriteLine("### JSON REsult: " + json);
                ITraceWriter writer = new MemoryTraceWriter();
                JsonSerializerSettings settings = new JsonSerializerSettings
                {
                    TraceWriter = writer
                };
                try
                {
                    data = JsonConvert.DeserializeObject<List<T>>(json, settings);
                }
                catch (System.Exception ex)
                {
                    Debug.WriteLine("### Exception " + ex.Message);
                    Debug.WriteLine("### Trace Results: " + writer);

                }
            }
            return data;
        }
1
So I digged depper into it. What I am currently searching for is the best strategy: 1. A custom IReferenceResolver: Does this really help my task? I have seen the one at stackoverflow.com/questions/38964774/… however this "only" seems to work for one type whereas I have 3 different ones..? 2. Write a completely custom JSON Renderer, who parses the example..? 3. Rewrite the backend to only always serialize one class, then call the REST service for a subclass... 4. Other alternatives?Wolffi82
Using a tool like QuickType will make your life a lot easier here.Abion47

1 Answers

0
votes

As you already figured out, you should use a custom IReferenceResolver.

You asked in the comments how can it be done with multiple types. You can use the resolver code found here to do that. This link includes a full example:

<script src="https://gist.github.com/carlin-q-scott/4c8a9cce734fa5b10a97.js"></script>

Although it doesn't use multiple types, it's easy to demonstrate how it'd used in such a case.

For example, in addition to Person, we can add a Cat type:

public class Cat
{
    public string Name {get; set;}
}

then add it as a property of Person:

public class Person
{
  public string Name { get; set;}
  public string PhoneNumber { get; set;}
  public Person Partner { get; set;}
  public Cat Cat { get; set;}
}

and then change the json into:

[
  {
     ""$id"": ""Jane Doe"",
    ""Name"": ""Jane Doe"",
    ""Cat"": {
     ""$id"": ""Johny"",
      ""Name"": ""Johny"",
    },
    ""PhoneNumber"": ""(555)555-5555"",
    ""Partner"": {
      ""$id"": ""John Doe"",
      ""Name"": ""John Doe"",
      ""PhoneNumber"": ""(555)555-5555"",
      ""Partner"": {
        ""$ref"": ""Jane Doe""
      },
      ""Cat"": {
         ""$ref"": ""Johny""
        }
    }
  },
  {
    ""$ref"": ""John Doe"",
  }
]

and it would work the same, as in this fiddle (note that instead of using ReferenceResolverProvider (as in the link) it uses ReferenceResolver (which is obselete), and it's because it causes some strange error here; Anyway, you should use the former).