aspnetcore: HttpClient.PostJsonAsync deserialize json failed

Describe the bug

When I use below code in razor page to post a http request. The deserialized object is not correct. There is no exception happened.

billInfo = await Http.PostJsonAsync<BillInfo>("api/TMobileBill", filePath);

I then changed to use Http.PostAsync to get the response string, then deserialize it by using Newtonsoft library. It works fine.

        var myContent = Newtonsoft.Json.JsonConvert.SerializeObject(filePath);
        var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
        var byteContent = new ByteArrayContent(buffer);
        byteContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json");
        var response = await Http.PostAsync("api/TMobileBill", byteContent);
        var responseStr = await response.Content.ReadAsStringAsync();
        billInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<BillInfo>(responseStr);
        StateHasChanged();

The response json string in my problem is:

{"period":"May 09, 2019 - Jun 08, 2019","dueDate":"Jul 01, 2019","totalAmount":326.03,"billInfoPerPerson":[{"name":"A B","phonesInfo":[{"phoneNumber":"(425) 111-1111","planHost":false,"usage":4.84,"regularCharge":30.0,"otherCharge":0.0,"totalCharge":0.0},{"phoneNumber":"(425) 222-2222","planHost":false,"usage":5.48,"regularCharge":30.0,"otherCharge":0.0,"totalCharge":0.0}],"totalAmount":60.0},{"name":"C D","phonesInfo":[{"phoneNumber":"(425) 333-3333","planHost":false,"usage":1.08,"regularCharge":20.0,"otherCharge":0.0,"totalCharge":0.0},{"phoneNumber":"(425) 444-4444","planHost":false,"usage":1.47,"regularCharge":20.0,"otherCharge":0.0,"totalCharge":0.0}],"totalAmount":40.0},{"name":"E F","phonesInfo":[{"phoneNumber":"(425) 555-5555","planHost":false,"usage":1.05,"regularCharge":20.0,"otherCharge":28.03,"totalCharge":0.0},{"phoneNumber":"(425) 666-6666","planHost":false,"usage":2.15,"regularCharge":30.0,"otherCharge":0.0,"totalCharge":0.0}],"totalAmount":78.03},{"name":"G H","phonesInfo":[{"phoneNumber":"(425) 777-7777","planHost":false,"usage":1.52,"regularCharge":20.0,"otherCharge":17.5,"totalCharge":0.0},{"phoneNumber":"(425) 888-8888","planHost":true,"usage":5.27,"regularCharge":30.0,"otherCharge":20.5,"totalCharge":0.0}],"totalAmount":88.0},{"name":"I J","phonesInfo":[{"phoneNumber":"(425) 999-9999","planHost":false,"usage":3.67,"regularCharge":30.0,"otherCharge":0.0,"totalCharge":0.0},{"phoneNumber":"(425) 000-0000","planHost":false,"usage":3.05,"regularCharge":30.0,"otherCharge":0.0,"totalCharge":0.0}],"totalAmount":60.0}],"warning":"There are $3 account service fee. Make sure it is expected. "}

The corresponding class is:

    public class PersonBillInfo
    {
        public string Name;
        public List<PhoneBillInfo> PhonesInfo;
        public float TotalAmount;
    }

    public class PhoneBillInfo
    {
        public string PhoneNumber;
        public bool PlanHost;
        public float Usage;
        public float RegularCharge;
        public float OtherCharge;
        public float TotalCharge;
    }

    public class BillInfo
    {
        public string Period;
        public string DueDate;
        public float TotalAmount;
        public IEnumerable<PersonBillInfo> BillInfoPerPerson;
        public string Warning;
    }

To Reproduce

Steps to reproduce the behavior:

  1. Using this version of ASP.NET Core ‘SDK 3.0.100-preview6-012264’
  2. Implement a controller action to respond the json string as above.
  3. From razor page, use below code to call the controller action.
var billInfo = Http.PostJsonAsync<BillInfo>("api/TMobileBill", filePath);
  1. Evaluate the billInfo object. All the members of billInfo object are null.

Expected behavior

The members of billInfo object have values.

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 19 (9 by maintainers)

Most upvoted comments

@yan0lovesha I believe the issue here is that System.Text.Json doesn’t support doing serialization with public fields. Can you try using public properties instead?

Here’s what I tried with the settings PostAsJsonAsync currently uses and it works fine:

static void Main(string[] args)
{
    var jsonString = "{\"period\":\"May 09, 2019 - Jun 08, 2019\",\"dueDate\":\"Jul 01, 2019\",\"totalAmount\":326.03}";

    var jsonObjFromDotnetCore = JsonSerializer.Parse<BillInfo>(jsonString, new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase});
    System.Console.WriteLine(jsonObjFromDotnetCore.Period);
}

public class BillInfo
{
    public string Period { get; set; }
    public string DueDate { get; set; }
    public float TotalAmount { get; set; }
}

I’d recommend filing an issue in https://github.com/dotnet/corefx/issues to consider adding support for public fields in a future release.

Since the DateTimeOffset within the JSON is not ISO 8601 compliant (it contains spaces), the JsonSerializer throws an exception (see below, at least on preview 7). If the contents of PublishDate was changed to ""2019-05-17T16:19:34-08:00"", it works as expected (and the object is deserialized fully).

image

public class BlogPostMetadata
{
    public bool IsDraft { get; set; }
    public DateTimeOffset PublishDate { get; set; }
    public string Title { get; set; }
    public string HeroImageFile { get; set; }
    public string SummaryMarkdownFile { get; set; }
    public string MarkdownFile { get; set; }
    public string SocialSharingFile { get; set; }
}

[Fact]
public static void TestingStuff()
{
    string temp = @"{
        ""Title"": ""Hello World"",
        ""PublishDate"": ""2019-05-17 16:19:34 -08:00"", // spaces don't work
        ""HeroImageFile"": ""heroimage.jpg"",
        ""SummaryMarkdownFile"": ""summary.md"",
        ""MarkdownFile"": ""post.md"",
        ""SocialSharingFile"": ""socialshare.html""
    }";

    BlogPostMetadata foo = JsonSerializer.Parse<BlogPostMetadata>(temp);
    Assert.Equal("Hello World", foo.Title);
}
System.Text.Json.JsonException : The JSON value could not be converted to System.DateTimeOffset. Path: $.PublishDate | LineNumber: 2 | BytePositionInLine: 45.

cc @steveharter, @layomia

@pranavkm the JSON is coming from a file (wwwroot content) as shown in the example above. The JSON property names match the C# property names.