Search code examples
c#jsonxamarin.formsgoogle-oauthxamarin.auth

Problem deserializing Google profile response


I'm authenticating to google in a Xamarin.Forms app. Once authenticated I want to retrieve user email, and use this code that I adapted from this blog, the code for retrieving the email:

public class GoogleProfile
{
    [Preserve]
    [JsonConstructor]
    public GoogleProfile() { }

    public string sub { get; set; }
    public string name { get; set; }
    public string given_name { get; set; }
    public string profile { get; set; }
    public string picture { get; set; }
    public string email { get; set; }
    public string email_verified { get; set; }
    public string locale { get; set; }
}

public class GoogleService
{
    public async Task<string> GetEmailAsync(string tokenType, string accessToken)
    {
        var httpClient = new HttpClient();
        httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(tokenType, accessToken);
        var json = await httpClient.GetStringAsync("https://www.googleapis.com/oauth2/v3/userinfo");//https://www.googleapis.com/userinfo/email?alt=json");
        var profile = JsonConvert.DeserializeObject<GoogleProfile>(json);
        return profile.email;
    }
}

Now, with a breakpoint in the last line return profile.email I've "seen" the json file that is this way:

{
  "sub": "",
  "name": "",
  "given_name": "",
  "profile": "",
  "picture": "",
  "email": "",
  "email_verified": "",
  "locale": "",
}

Between the quotes are the data, obviously.

I'm not really accustomed to JSon but reading this I thought that the format is simply "nameOfProperty":"ValueOfProperty", so I made GoogleProfile object.

I read too that the attributes [Preserve] and [JsonConstructor] are necessary so the linker doesn't just "remove" empty constructors when compiling.

There are no exceptions and no apparent problems, but if I put a breakpoint in the last return profile.email; line, I can see the json object has all the data and is perfectly fine, but the profile object have only the email property with value null...

I don't understand it: what happened with the other properties? Is it even possible? You know, you code an object with a bunch of properties but the object are created with only one of those properties? If the object had all the properties with a value of null, Ok, but where did the other properties gone?

Just to be sure, I've cleaned and rebuild projects and solution, I've removed bin and obj folders and then clean and rebuild again... I've done the obvious things.

Thing is that as you can see in the blog I mentioned earlier, first I didn't use GoogleProfile object, I just copied the object in the blog... that has only one property called email. Is it possible that visual studio or xamarin or something got bugged and the changes didn't got reflected? I doubt it because I've changed the URI used in the GetStringAsync method and it did got reflected, but well, I don't know.

PD: In the meanwhile I'm parsing the JSON directly as a normal string, it's a simple object and I only really need the email, but well, it would be a shame that I'd have to parse it that way instead of just deserialize it.


Edit: As suggested by Skin in the comments I've used http://jsonutils.com/

One of the properties was a bool (of course), so I've changed it and the object now is:

public class GoogleProfile
{
    [Preserve]
    [JsonConstructor]
    public GoogleProfile() { }

    public string sub { get; set; }
    public string name { get; set; }
    public string given_name { get; set; }
    public string profile { get; set; }
    public string picture { get; set; }
    public string email { get; set; }
    public bool email_verified { get; set; }
    public string locale { get; set; }
}

But it didn't change the result, still happening the same. Image of the result at the breakpoint:

Breakpoint values


Solution

  • I don't think there is any problem with your code. I used the same code of you and didn't change anything. I use my own tokenType and accessToken.

    My steps:

    1. copy your code to my project.
    2. get my own token by using the project in the blog
    3. install newtonsoft Nuget package
    4. run the project and get result

    So, is there any problem with your account? Did you account have an e-mail? Any difference with mine?

    Here is what I get:

    json Result

    Code:

    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
    
            InitializeComponent();
    
            testAsync();
        }
    
        public async Task testAsync()
        {
    
            GoogleService googleS = new GoogleService();
            // use your token here
            var email = await googleS.GetEmailAsync("", "");
            Console.WriteLine(email);
        }
    
        public class GoogleProfile
        {
            [Preserve]
            [JsonConstructor]
            public GoogleProfile() { }
    
            public string sub { get; set; }
            public string name { get; set; }
            public string given_name { get; set; }
            public string profile { get; set; }
            public string picture { get; set; }
            public string email { get; set; }
            public string email_verified { get; set; }
            public string locale { get; set; }
        }
    
        public class GoogleService
        {
            public async Task<string> GetEmailAsync(string tokenType, string accessToken)
            {
                var httpClient = new HttpClient();
                httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
                var json = await httpClient.GetStringAsync("https://www.googleapis.com/oauth2/v3/userinfo");//https://www.googleapis.com/userinfo/email?alt=json");
                var profile = JsonConvert.DeserializeObject<GoogleProfile>(json);
    
                Console.WriteLine(json);
                Console.WriteLine(profile);
    
    
                return profile.email;
            }
        }
    }