Updated October 2026 for .NET 10 (LTS) and System.Text.Json 10.
This error means the JSON has a value whose type does not match your C# property. Most often a number or true/false arrives where your class expects a string. System.Text.Json does not convert between types silently, so it stops. Below are the common variants, why they happen, and three ways to fix them.
The error
System.Text.Json.JsonException: The JSON value could not be converted to System.String. Path: $.EmployeeId | LineNumber: 3 | BytePositionInLine: 22.
The same message appears for other types, such as System.DateTime, System.Boolean or System.Collections.Generic.List`1. The Path part tells you exactly which property failed.
Why it happens
Here the JSON sends numbers and a boolean:
{
"FirstName": "Ann",
"LastName": "Lee",
"EmployeeId": 1042,
"IsPermanent": true
}
But the class declares them as strings:
public class Customer
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
public string EmployeeId { get; set; } = ""; // JSON has a number
public string IsPermanent { get; set; } = ""; // JSON has true/false
}
var customer = JsonSerializer.Deserialize<Customer>(json); // throws
Newtonsoft.Json quietly turns 1042 into "1042", so this often shows up after switching to System.Text.Json. Other common causes for other types:
- System.DateTime: the date is not in ISO 8601 format, for example
"21/10/2026"instead of"2026-10-21". - List or array: the JSON has a single object
{ }where the class expects an array[ ]. - Nested object: the JSON has a string where the class expects an object.
Fix 1: Match your C# types to the JSON (best fix)
If EmployeeId is always a number, make it a number in C#:
public class Customer
{
public string FirstName { get; set; } = "";
public string LastName { get; set; } = "";
public int EmployeeId { get; set; }
public bool IsPermanent { get; set; }
}
The quickest way to get matching types is to paste a sample of the JSON into the free C# Class Converter. It generates classes with the right types for every field.
Fix 2: Accept numbers and booleans in string properties
If the sender is inconsistent (sometimes "1042", sometimes 1042) and the property must stay a string, add a small converter:
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
public sealed class FlexibleStringConverter : JsonConverter<string>
{
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
reader.TokenType switch
{
JsonTokenType.String => reader.GetString(),
JsonTokenType.Number => Encoding.UTF8.GetString(reader.ValueSpan), // keeps the exact digits
JsonTokenType.True => "true",
JsonTokenType.False => "false",
JsonTokenType.Null => null,
_ => throw new JsonException($"Cannot read {reader.TokenType} as a string.")
};
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
writer.WriteStringValue(value);
}
Use it for one property:
[JsonConverter(typeof(FlexibleStringConverter))]
public string EmployeeId { get; set; } = "";
Or for every string property:
var options = new JsonSerializerOptions();
options.Converters.Add(new FlexibleStringConverter());
var customer = JsonSerializer.Deserialize<Customer>(json, options);
In ASP.NET Core, register it once in Program.cs so request bodies use it too:
// Controllers
builder.Services.AddControllers()
.AddJsonOptions(o => o.JsonSerializerOptions.Converters.Add(new FlexibleStringConverter()));
// Minimal APIs
builder.Services.ConfigureHttpJsonOptions(o =>
o.SerializerOptions.Converters.Add(new FlexibleStringConverter()));
More on writing converters: Custom converters with System.Text.Json.
Fix 3: Use Newtonsoft.Json for that API
If you are migrating a large API that relied on Newtonsoft’s loose conversions, you can keep Newtonsoft for now and move over gradually:
// dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
builder.Services.AddControllers().AddNewtonsoftJson();
See Add Newtonsoft.Json support in ASP.NET Core. For new code, prefer Fix 1.
FAQ
Is there a built-in option for numbers into strings?
No. JsonNumberHandling.AllowReadingFromString works the other way round: it lets a quoted number like "42" fill an int property. To read a number into a string, use a converter as in Fix 2.
How do I find which property failed?
Read the Path in the exception message, for example $.Address.ZipCode. It points to the exact JSON property, including nested ones.
Related
- The JSON value could not be converted to System.Int32
- System.Text.Json “is an invalid start of a property name”
- Serialize and deserialize with System.Text.Json
- camelCase and PascalCase with System.Text.Json
Sources: Microsoft Learn: Write custom converters · Microsoft Learn: Migrate from Newtonsoft.Json
