Share via

Question about JSON (De)Serialization using System.Text.Json.

WoodManEXP 80 Reputation points
2026-06-04T19:56:26.8533333+00:00

This is a Q about JSON (De)Serialization using System.Text.Json.

There are three classes involved, abbreviated here:

    internal class FolderState
    {
        /// <summary>
        /// Stack elemens
        /// </summary>
        /// 
        [JsonInclude] public int LastFileNum { get; set; }
        [JsonInclude] public int LastFolderNum { get; set; }
        [JsonInclude] public String Path { get; set; }
		.
		.
		.
	}
	
	internal class FolderStack<T> : Stack<T>
    {
        public FolderStack()
        {
        }
    }
	
	// For non-reflection JSON (De)Serialization
    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(FolderStack<FolderState>))]
    [JsonSerializable(typeof(FolderState))]
    internal partial class SourceGenerationContext : JsonSerializerContext { }
	
	internal class FolderStateStack
    {
        public FolderStack<FolderState> FolderStack { get; set; }
		.
		.
		.
	}

The FolderStateStack class has a member FolderStack which holds a stack of FolderState objects. Also added are the markers, described in How to use source generation in System.Text.Json - .NET | Microsoft Learn, to enable source-code generation mode (instead of reflection mode).

The FolderStack is serialized to JSON like this

String jsonStr = JsonSerializer.Serialize(FolderStack, SourceGenerationContext.Default.FolderStackFolderState);

Which results in this JSON

[
  {
    "LastFileNum": 0,
    "LastFolderNum": -1,
    "Path": "C:\\Users\\William\\Pictures"
  }
]

The attempt to deserialize the JSON codes like this

FolderStack = JsonSerializer.Deserialize(fileStream, SourceGenerationContext.Default.FolderStackFolderState);

This results in an exception, it seems because the fields do not match, the JSON does not fully define the FolderState elements.

In order to Deserialize the JSON it should look more like this, with the FolderState elements labeled?

[
	"FolderState":
	[
  		{
    		"LastFileNum": 0,
    		"LastFolderNum": -1,
    		"Path": "C:\\Users\\Robert\\Pictures"
  		}
	]
]

Any thoughts on how to correct the Serialization so that the FolderState is labeled??

TY!!

Developer technologies | C#
Developer technologies | C#

An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.


Answer accepted by question author

Jay Pham (WICLOUD CORPORATION) 3,855 Reputation points Microsoft External Staff Moderator
2026-06-05T06:39:24.1766667+00:00

Hi @WoodManEXP ,

Your JSON is actually fine the way it is, and you don't need to label the elements. A FolderStack<FolderState> is just a stack of objects, so System.Text.Json correctly writes it as a plain array:


[

  { "LastFileNum": 0, "LastFolderNum": -1, "Path": "C:\\Users\\William\\Pictures" }

]

That [ "FolderState": [ ... ] ] shape you were considering isn't valid JSON, so don't go that route. The serializer matches the object properties by name on the way back in, so the array is exactly what it expects.

I reproduced your setup (the custom FolderStack<T> : Stack<T>, source generation, the same context) and deserializing from a string works perfectly. So the type and the markers aren't the problem.

What does throw is reusing the same FileStream for both writing and reading. After you serialize into the stream, its position is sitting at the end of the file. When you then call Deserialize on that same stream, there are no bytes left to read, and you get:

System.Text.Json.JsonException: The input does not contain any JSON tokens. Expected the input to start with a valid JSON token...

That matches the exception you're hitting. Two ways to fix it:


// Rewind the existing stream before reading

fileStream.Position = 0;

FolderStack = JsonSerializer.Deserialize(fileStream, SourceGenerationContext.Default.FolderStackFolderState);


// Open a fresh read stream 

using var readStream = File.OpenRead(path);

FolderStack = JsonSerializer.Deserialize(readStream, SourceGenerationContext.Default.FolderStackFolderState);

One more thing worth knowing, since it bites a lot of people: a stack round-trips in reversed order. If you push A then B, the JSON comes out as [B, A], but when you deserialize, the items get pushed back in that array order, so A ends up on top instead of B. If the original top-of-stack matters to you, either reverse the collection after loading, or store/load a List<FolderState> and rebuild the stack in the order you want.

Hope this helps! If my explanation and the information I provided were helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.

Was this answer helpful?

2 people found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. AI answer

    2026-06-04T19:56:43.69+00:00

    System.Text.Json does not require or support “labeling” the element type inside a JSON array. The JSON produced by

    [
      {
        "LastFileNum": 0,
        "LastFolderNum": -1,
        "Path": "C:\\Users\\William\\Pictures"
      }
    ]
    

    is already the correct representation for FolderStack<FolderState> (a stack of FolderState objects). The deserialization error is not because FolderState is unlabeled.

    Key points and fixes:

    1. Array of objects is the correct shape
      JsonSerializer.Deserialize<FolderStack<FolderState>>(...) expects a JSON array of objects whose properties match FolderState. Extra properties in JSON are ignored by default; missing properties are allowed unless marked as required. There is no need for:
         [
           FolderState:
           [
             { ... }
           ]
         ]
      
      That is not valid JSON and will not be understood by System.Text.Json.
    2. Use the generated type info correctly
      With source generation, the correct call for a FolderStack<FolderState> is:
         // Serialize
         string jsonStr = JsonSerializer.Serialize(
             folderStateStack.FolderStack,
             SourceGenerationContext.Default.FolderStackFolderState);
         
         // Deserialize from string
         folderStateStack.FolderStack = JsonSerializer.Deserialize(
             jsonStr,
             SourceGenerationContext.Default.FolderStackFolderState);
      
      When deserializing from a stream:
         using var fileStream = File.OpenRead(path);
         folderStateStack.FolderStack = JsonSerializer.Deserialize(
             fileStream,
             SourceGenerationContext.Default.FolderStackFolderState);
      
      The second parameter (FolderStackFolderState) is the generated JsonTypeInfo<FolderStack<FolderState>>, which matches the JSON array of FolderState objects.
    3. Ensure the generic type is included in the context
      The context already includes:
         [JsonSerializable(typeof(FolderStack<FolderState>))]
         [JsonSerializable(typeof(FolderState))]
         internal partial class SourceGenerationContext : JsonSerializerContext { }
      
      This is correct. No additional labeling is required in JSON.
    4. Check for other causes of the exception
      Since System.Text.Json ignores unknown JSON properties and allows missing ones by default, the usual causes of exceptions here are:
      • The JSON is not valid (trailing commas, comments, or the “FolderState:” text).
      • The stream is not positioned at the beginning (e.g., reused stream without Position = 0).
      • A different type is being deserialized than the one registered in the context.
      Ensure the JSON is exactly the array shown above and that the deserialization call uses the same type info that was used for serialization.

    In summary, the JSON shape is already correct; remove any extra labels like FolderState: and ensure the deserialization uses SourceGenerationContext.Default.FolderStackFolderState against a valid JSON array.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

    1 person found this answer helpful.

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.