System.Text.Json - Deserialize nested object as string
Solution 1:
Found a right way how to correctly read the nested JSON object inside the JsonConverter
. The complete solution is the following:
public class SomeModel
{
public int Id { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(InfoToStringConverter))]
public string Info { get; set; }
}
public class InfoToStringConverter : JsonConverter<string>
{
public override string Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using (var jsonDoc = JsonDocument.ParseValue(ref reader))
{
return jsonDoc.RootElement.GetRawText();
}
}
public override void Write(
Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
}
In the code itself there is no need even to create options:
var json = @"{
""Id"": 1,
""Name"": ""Some Name"",
""Info"": {
""Additional"": ""Fields"",
""Are"": ""Inside""
}
}";
var model = JsonSerializer.Deserialize<SomeModel>(json);
The raw JSON text in the Info
property contains even extra spaces introduced in the example for nice readability.
And there is no mixing of model representation and its serialization as remarked @PavelAnikhouski in his answer.
Solution 2:
You can use a JsonExtensionData
attribute for that and declare a Dictionary<string, JsonElement>
or Dictionary<string, object>
property in your model to store this information
public class SomeModel
{
public int Id { get; set; }
public string Name { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement> ExtensionData { get; set; }
[JsonIgnore]
public string Data
{
get
{
return ExtensionData?["Info"].GetRawText();
}
}
}
Then you can add an additional property to get a string from this dictionary by Info
key. In code above the Data
property will contain the expected string
{
"Additional": "Fields",
"Are": "Inside"
}
For some reasons adding the property with the same name Info
doesn't work, even with JsonIgnore
. Have a look at Handle overflow JSON for details.
You can also declare the Info
property as JsonElement
type and get raw text from it
public class SomeModel
{
public int Id { get; set; }
public string Name { get; set; }
public JsonElement Info { get; set; }
}
var model = JsonSerializer.Deserialize<SomeModel>(json);
var rawString = model.Info.GetRawText();
But it will cause a mixing of model representation and its serialization.
Another option is to parse the data using JsonDocument
, enumerate properties and parse them one by one, like that
var document = JsonDocument.Parse(json);
foreach (var token in document.RootElement.EnumerateObject())
{
if (token.Value.ValueKind == JsonValueKind.Number)
{
if(token.Value.TryGetInt32(out int number))
{
}
}
if (token.Value.ValueKind == JsonValueKind.String)
{
var stringValue = token.Value.GetString();
}
if (token.Value.ValueKind == JsonValueKind.Object)
{
var rawContent = token.Value.GetRawText();
}
}
Solution 3:
a quick addendum to the accepted answer:
If you need to write raw JSON values as well, here is an implementation of the Write
method for the converter:
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
{
using (JsonDocument document = JsonDocument.Parse(value))
{
document.RootElement.WriteTo(writer);
}
}
As outlined in the dotnet runtime repo on github, this seems to be the "proper" way to workaround the fact that they decided not to implement a WriteRawValue
method.