Reputation: 398
Given this model class:
using System.Text.Json;
public class QueueMessage
{
public int MerchantId { get; }
public string Message { get; }
}
When I try to deserialize a json string into the type QueueMessage, the fields are set to default. 0 and null. This is how I've tried to deserialize it:
var jsonString = "{\"MerchantId\":2,\"Message\":\"Message 2\"}";
QueueMessage message = JsonSerializer.Deserialize<QueueMessage>(jsonString, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
message.Message is null
message.MerchantId is 0
I'm using .Net 5 by the way.
What have I tried Well i try to use my ol' buddy Newtonsoft.Json
dynamic message = JsonConvert.DeserializeObject(jsonString);
dynamic mercId = message.MerchantId.Value; //THIS gives the expected value of 2
However,
QueueMessage msg = JsonConvert.DeserializeObject<QueueMessage>(jsonString);
Gives the 0, null result
Now, the question is why does deserializing to a typed object fail?
Upvotes: 1
Views: 1124
Reputation: 155055
As an alternative to Ziv's answer, and if you value the benefits of constructed types and/or immutability, recent versions of System.Text.Json
now support \[JsonConstructor\]
, so you can now use constructed DTO types.
In my opinion you should always also specify an explicit [JsonPropertyName]
to protect against DTO/JSON breakages caused by renamed properties or changing project-wide camelCase
vs PascalCase
JSON serialization settings.
camelCase
, not PascalCase
as that's the capitalization convention used by JavaScript/TypeScript, which is the usual target for JSON types (unfortunately STJ defaults to PascalCase
(*grrrr*))using System.Text.Json;
public class QueueMessage
{
[JsonConstructor]
public QueueMessage(
int merchantId,
string? message
)
{
this.MerchantId = merchantId;
this.Message = message;
}
[JsonPropertyName("merchantId")] public int MerchantId { get; }
[JsonPropertyName("message") ] public string? Message { get; }
}
QueueMessage.Message
from ever being null
, so the String?
property can be changed to String
which makes consuming the DTO nicer - and it can also validate MerchantId
too:using System.Text.Json;
public class QueueMessage
{
[JsonConstructor]
public QueueMessage(
int merchantId,
string message
)
{
this.MerchantId = merchantId > 0 ? merchantId : throw new ArgumentOutOfRangeException( paramName: nameof(merchantId), actualValue: merchantId, message: "Value must be positive and non-zero." );
this.Message = message ?? throw new ArgumentNullException(nameof(message));
}
/// <summary>Always >= 1.</summary>
[JsonPropertyName("merchantId")] public int MerchantId { get; }
/// <summary>Never null.</summary>
[JsonPropertyName("message") ] public string Message { get; }
}
record class
record class
.
Message
is now String?
again.[property: ]
to apply JsonPropertyName
and other attributes to record class
properties via their ctor parameter names.public record class QueueMessage(
[property: JsonPropertyName("merchantId")] int MerchantId,
[property: JsonPropertyName("message") ] string? Message
);
Upvotes: 2
Reputation: 4516
You need to add setters to your properties, otherwise the deserializer can't assign values to those properties
public class QueueMessage
{
public int MerchantId { get; set; }
public string Message { get; set; }
}
Upvotes: 0