I found this question/answer Deserializing a json array into a object C# using System.Text.Json. But the answer doesn't fully work for me. The issue is that once I try to access the list from inside class Bar, data
is null.
JSON :
[
{
"param1":"value",
"param2":"value2"
},
{
"param1":"value",
"param2":"value2"
}
]
Class objects:
public class Bar : List<Foo>
{
public List<Foo> data { get; set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach( Foo item in data )
{
sb.Append( item.ToString() );
}
return sb.ToString();
}
}
public class Foo
{
public string param1 { get; set; }
public string param2 { get; set; }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append( " param 1 : " + param1 + Environment.NewLine );
sb.Append( " param 2 : " + param2 + Environment.NewLine );
return sb.ToString();
}
}
Calling it:
string? director = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
string configfile = String.Empty;
if ( string.IsNullOrEmpty( director ) == false )
{
configfile = Path.Combine( director, "ArrrayTest.txt" );
if ( ( string.IsNullOrEmpty( configfile ) == false ) && ( File.Exists( configfile ) == true ) )
{
using ( StreamReader sr = File.OpenText( configfile ) )
{
string jsonstring = sr.ReadToEnd();
Bar? output = JsonSerializer.Deserialize<Bar>( jsonstring );
if ( output != null )
{
Console.WriteLine(output.ToString() );
}
}
}
At the line if ( output != null )
output is not null and data
contains two elements. But inside output.ToString();
data
is null.
How do I make this work, so that inside the Bar class, data
isn't null? Why would data
be different when accessed from outside, then when accessed from inside the object?
I'm using VS 2022, .NET 6 and System.Text.json.
The issue is that your Bar
class is both a List<Foo>
and contains a property List<Foo> data
. This design is confusing because it's unclear where the JSON array should be deserialized to – the Bar
object itself (since it's a List<Foo>
) or the data
property inside it.
When you call JsonSerializer.Deserialize<Bar>(jsonstring);
, the serializer tries to match the JSON array to the Bar
class. Since Bar
is a List<Foo>
, it successfully populates the Bar
instance itself with the Foo
items from the array. However, the data
property remains null because there's no corresponding property in the JSON array to map it to.
The solution is to clarify the design. If Bar
should contain the list, then it shouldn't inherit from List<Foo>
. If Bar
should inherit from List<Foo>
, then you don't need the data
property.
Here's how you can adjust your Bar
class:
Option 1: Remove inheritance and use the data
property only.
public class Bar
{
// No longer inheriting from List<Foo>
public List<Foo> data { get; set; } = new List<Foo>(); // Initialize so it's never null
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (Foo item in data) // Now it uses the 'data' property
{
sb.Append(item.ToString());
}
return sb.ToString();
}
}
With this option, you don't have to change your deserialization code. The JSON array will correctly populate the data
property.
Option 2: Remove the data
property and use the Bar
class itself.
public class Bar : List<Foo>
{
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (Foo item in this) // 'this' refers to the Bar itself as it's a List<Foo>
{
sb.Append(item.ToString());
}
return sb.ToString();
}
}
For option 2, your calling code should work as it is, since Bar
itself is the list, and all items in the deserialized JSON array are placed in Bar
directly.
After adjusting your Bar
class according to one of these options, the data
should not be null when accessed within the Bar
class' ToString()
method. The confusion arises from trying to use both inheritance and a property to represent the same data.
Remember to update all other parts in your code that might depend on Bar
being a List<Foo>
or having a data
property to match your chosen design.