Skip to content

Migration guide, API renames and read-path fixes - #210

Open
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes
Open

Migration guide, API renames and read-path fixes#210
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes

Conversation

@manuc66

Copy link
Copy Markdown
Owner

Problem

  • No migration guide existed for Newtonsoft -> STJ or between the STJ engines.
  • The STJ FallBack* names had inconsistent capitalization.
  • The AOT generator's nested-chain write path duplicated discriminators; the base-as-leaf read path parsed the document twice.

Fix

  • Add MIGRATION.md (Newtonsoft -> STJ and between STJ engines).
  • Rename FallBackSubTypeAttribute / FallBackToNearestAncestor in JsonSubTypes.Text.Json.
  • Fix duplicate discriminators in the AOT generator's nested-chain write path.
  • Eliminate the second JsonDocument parse on the base-as-leaf read path.
  • Prefix benchmark methods per scenario.

Tests

  • Full STJ + AOT + generator suites pass; the golden master is regenerated to match the generator change (net8.0 and net10.0).

Honest note(s)

  • The FallBack* rename is a breaking API change in the experimental STJ package; per the README the public API is not frozen before 1.0.0.

MIGRATION.md is an actionable before/after recipe: the mechanical renames, the
behaviors that actually differ between the Newtonsoft and STJ packages, and the
capability ceilings when moving between the converter, resolver and generator.
Also fix the outdated test count in the STJ status note and link the guide from
the README.
…SubTypes.Text.Json
The STJ package is still a release candidate, so now is the time to fix the
capitalization inherited from the Newtonsoft API: FallBackSubTypeAttribute ->
FallbackSubTypeAttribute and FallBackToNearestAncestor() ->
FallbackToNearestAncestor(). The Newtonsoft package keeps its historical names.
The generator matches attributes by namespace + short name, so it now looks up
FallbackSubTypeAttribute; the native JsonUnknownDerivedTypeHandling enum member
is untouched. All STJ (196), AOT (79) and Newtonsoft (153) tests pass.
…e path
When a nested hierarchy's discriminators are also native properties of the type
(e.g. [JsonPropertyName("$PayloadKind")]), the generated nested-chain writer
emitted every payload property without excluding the discriminator names, so the
injected discriminators were written twice. The runtime converter already
excluded them; the generator now skips any property whose name matches a
discriminator in the chain.
Adds a dedicated test fixture pinning the single-write and the round-trip to the
deepest subtype.
Converter_Serialize/Generated_Deserialize etc. collided across the single-object
and base-as-leaf classes, so a full-suite run produced ambiguous rows. Each class
now uses a unique scenario prefix (Single_, Col_, Nested_, Pres_, Leaf_, Nw_)
and PERFORMANCE.md documents the mapping and how to filter a scenario.
ReadObject parsed the JSON once to resolve the type, then ReadPlainObject parsed
it again from the reader to materialize the base object. Reuse the already-parsed
RootElement instead, matching how the subtype path deserializes.
Measured (BenchmarkDotNet, net10, DefaultJob): Leaf_Converter_Deserialize
1.58us / 560 B before, 1.16us / 360 B after. All STJ and AOT tests pass.
The nested-chain writer now skips properties whose name matches a
discriminator in the chain; the committed PayloadJsonSubTypesConverter
still carried the old output without the guard, failing the golden-master
test.
…ribute-based discriminator
The JsonSubTypeConverterAttribute kept passing a closed converter type to
JsonConverterAttribute, so System.Text.Json built the converter through its
parameterless constructor and the CreateConverter override was never called.
The attribute now leaves ConverterType null for the JsonSubtypes<T> forms and
routes through CreateConverter, which closes the generic over the annotated
type (new [JsonSubTypeConverter("Kind")] convenience constructors) and passes
the discriminator to the converter. As a result the attribute-based write path
now injects the discriminator for registered subtypes, matching the behaviour
the README and MIGRATION.md already documented. GetTypeResolver and
CreateTypeResolver close JsonSubtypes<> over the target type when the attribute
carries no converter type, so the resolver dance keeps working for nested
hierarchies.
Property-presence has no discriminator to inject, so serialization writes
the plain runtime contract. Add a parity test pinning that the runtime
converter and the generated converter emit the same output for a
property-presence subtype (all properties once, nothing injected), and
strengthen the attribute-based runtime test to assert the exact property
set so migrating never loses a property nor duplicates the discriminator.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@manuc66
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Migration guide, API renames and read-path fixes by manuc66 · Pull Request #210 · manuc66/JsonSubTypes · GitHub
Skip to content

Migration guide, API renames and read-path fixes - #210

Open
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes
Open

Migration guide, API renames and read-path fixes#210
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes

Conversation

@manuc66

Copy link
Copy Markdown
Owner

Problem

  • No migration guide existed for Newtonsoft -> STJ or between the STJ engines.
  • The STJ FallBack* names had inconsistent capitalization.
  • The AOT generator's nested-chain write path duplicated discriminators; the base-as-leaf read path parsed the document twice.

Fix

  • Add MIGRATION.md (Newtonsoft -> STJ and between STJ engines).
  • Rename FallBackSubTypeAttribute / FallBackToNearestAncestor in JsonSubTypes.Text.Json.
  • Fix duplicate discriminators in the AOT generator's nested-chain write path.
  • Eliminate the second JsonDocument parse on the base-as-leaf read path.
  • Prefix benchmark methods per scenario.

Tests

  • Full STJ + AOT + generator suites pass; the golden master is regenerated to match the generator change (net8.0 and net10.0).

Honest note(s)

  • The FallBack* rename is a breaking API change in the experimental STJ package; per the README the public API is not frozen before 1.0.0.

MIGRATION.md is an actionable before/after recipe: the mechanical renames, the
behaviors that actually differ between the Newtonsoft and STJ packages, and the
capability ceilings when moving between the converter, resolver and generator.
Also fix the outdated test count in the STJ status note and link the guide from
the README.
…SubTypes.Text.Json
The STJ package is still a release candidate, so now is the time to fix the
capitalization inherited from the Newtonsoft API: FallBackSubTypeAttribute ->
FallbackSubTypeAttribute and FallBackToNearestAncestor() ->
FallbackToNearestAncestor(). The Newtonsoft package keeps its historical names.
The generator matches attributes by namespace + short name, so it now looks up
FallbackSubTypeAttribute; the native JsonUnknownDerivedTypeHandling enum member
is untouched. All STJ (196), AOT (79) and Newtonsoft (153) tests pass.
…e path
When a nested hierarchy's discriminators are also native properties of the type
(e.g. [JsonPropertyName("$PayloadKind")]), the generated nested-chain writer
emitted every payload property without excluding the discriminator names, so the
injected discriminators were written twice. The runtime converter already
excluded them; the generator now skips any property whose name matches a
discriminator in the chain.
Adds a dedicated test fixture pinning the single-write and the round-trip to the
deepest subtype.
Converter_Serialize/Generated_Deserialize etc. collided across the single-object
and base-as-leaf classes, so a full-suite run produced ambiguous rows. Each class
now uses a unique scenario prefix (Single_, Col_, Nested_, Pres_, Leaf_, Nw_)
and PERFORMANCE.md documents the mapping and how to filter a scenario.
ReadObject parsed the JSON once to resolve the type, then ReadPlainObject parsed
it again from the reader to materialize the base object. Reuse the already-parsed
RootElement instead, matching how the subtype path deserializes.
Measured (BenchmarkDotNet, net10, DefaultJob): Leaf_Converter_Deserialize
1.58us / 560 B before, 1.16us / 360 B after. All STJ and AOT tests pass.
The nested-chain writer now skips properties whose name matches a
discriminator in the chain; the committed PayloadJsonSubTypesConverter
still carried the old output without the guard, failing the golden-master
test.
…ribute-based discriminator
The JsonSubTypeConverterAttribute kept passing a closed converter type to
JsonConverterAttribute, so System.Text.Json built the converter through its
parameterless constructor and the CreateConverter override was never called.
The attribute now leaves ConverterType null for the JsonSubtypes<T> forms and
routes through CreateConverter, which closes the generic over the annotated
type (new [JsonSubTypeConverter("Kind")] convenience constructors) and passes
the discriminator to the converter. As a result the attribute-based write path
now injects the discriminator for registered subtypes, matching the behaviour
the README and MIGRATION.md already documented. GetTypeResolver and
CreateTypeResolver close JsonSubtypes<> over the target type when the attribute
carries no converter type, so the resolver dance keeps working for nested
hierarchies.
Property-presence has no discriminator to inject, so serialization writes
the plain runtime contract. Add a parity test pinning that the runtime
converter and the generated converter emit the same output for a
property-presence subtype (all properties once, nothing injected), and
strengthen the attribute-based runtime test to assert the exact property
set so migrating never loses a property nor duplicates the discriminator.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@manuc66
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Migration guide, API renames and read-path fixes by manuc66 · Pull Request #210 · manuc66/JsonSubTypes · GitHub
Skip to content

Migration guide, API renames and read-path fixes - #210

Open
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes
Open

Migration guide, API renames and read-path fixes#210
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes

Conversation

@manuc66

Copy link
Copy Markdown
Owner

Problem

  • No migration guide existed for Newtonsoft -> STJ or between the STJ engines.
  • The STJ FallBack* names had inconsistent capitalization.
  • The AOT generator's nested-chain write path duplicated discriminators; the base-as-leaf read path parsed the document twice.

Fix

  • Add MIGRATION.md (Newtonsoft -> STJ and between STJ engines).
  • Rename FallBackSubTypeAttribute / FallBackToNearestAncestor in JsonSubTypes.Text.Json.
  • Fix duplicate discriminators in the AOT generator's nested-chain write path.
  • Eliminate the second JsonDocument parse on the base-as-leaf read path.
  • Prefix benchmark methods per scenario.

Tests

  • Full STJ + AOT + generator suites pass; the golden master is regenerated to match the generator change (net8.0 and net10.0).

Honest note(s)

  • The FallBack* rename is a breaking API change in the experimental STJ package; per the README the public API is not frozen before 1.0.0.

MIGRATION.md is an actionable before/after recipe: the mechanical renames, the
behaviors that actually differ between the Newtonsoft and STJ packages, and the
capability ceilings when moving between the converter, resolver and generator.
Also fix the outdated test count in the STJ status note and link the guide from
the README.
…SubTypes.Text.Json
The STJ package is still a release candidate, so now is the time to fix the
capitalization inherited from the Newtonsoft API: FallBackSubTypeAttribute ->
FallbackSubTypeAttribute and FallBackToNearestAncestor() ->
FallbackToNearestAncestor(). The Newtonsoft package keeps its historical names.
The generator matches attributes by namespace + short name, so it now looks up
FallbackSubTypeAttribute; the native JsonUnknownDerivedTypeHandling enum member
is untouched. All STJ (196), AOT (79) and Newtonsoft (153) tests pass.
…e path
When a nested hierarchy's discriminators are also native properties of the type
(e.g. [JsonPropertyName("$PayloadKind")]), the generated nested-chain writer
emitted every payload property without excluding the discriminator names, so the
injected discriminators were written twice. The runtime converter already
excluded them; the generator now skips any property whose name matches a
discriminator in the chain.
Adds a dedicated test fixture pinning the single-write and the round-trip to the
deepest subtype.
Converter_Serialize/Generated_Deserialize etc. collided across the single-object
and base-as-leaf classes, so a full-suite run produced ambiguous rows. Each class
now uses a unique scenario prefix (Single_, Col_, Nested_, Pres_, Leaf_, Nw_)
and PERFORMANCE.md documents the mapping and how to filter a scenario.
ReadObject parsed the JSON once to resolve the type, then ReadPlainObject parsed
it again from the reader to materialize the base object. Reuse the already-parsed
RootElement instead, matching how the subtype path deserializes.
Measured (BenchmarkDotNet, net10, DefaultJob): Leaf_Converter_Deserialize
1.58us / 560 B before, 1.16us / 360 B after. All STJ and AOT tests pass.
The nested-chain writer now skips properties whose name matches a
discriminator in the chain; the committed PayloadJsonSubTypesConverter
still carried the old output without the guard, failing the golden-master
test.
…ribute-based discriminator
The JsonSubTypeConverterAttribute kept passing a closed converter type to
JsonConverterAttribute, so System.Text.Json built the converter through its
parameterless constructor and the CreateConverter override was never called.
The attribute now leaves ConverterType null for the JsonSubtypes<T> forms and
routes through CreateConverter, which closes the generic over the annotated
type (new [JsonSubTypeConverter("Kind")] convenience constructors) and passes
the discriminator to the converter. As a result the attribute-based write path
now injects the discriminator for registered subtypes, matching the behaviour
the README and MIGRATION.md already documented. GetTypeResolver and
CreateTypeResolver close JsonSubtypes<> over the target type when the attribute
carries no converter type, so the resolver dance keeps working for nested
hierarchies.
Property-presence has no discriminator to inject, so serialization writes
the plain runtime contract. Add a parity test pinning that the runtime
converter and the generated converter emit the same output for a
property-presence subtype (all properties once, nothing injected), and
strengthen the attribute-based runtime test to assert the exact property
set so migrating never loses a property nor duplicates the discriminator.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@manuc66
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Migration guide, API renames and read-path fixes by manuc66 · Pull Request #210 · manuc66/JsonSubTypes · GitHub
Skip to content

Migration guide, API renames and read-path fixes - #210

Open
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes
Open

Migration guide, API renames and read-path fixes#210
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes

Conversation

@manuc66

Copy link
Copy Markdown
Owner

Problem

  • No migration guide existed for Newtonsoft -> STJ or between the STJ engines.
  • The STJ FallBack* names had inconsistent capitalization.
  • The AOT generator's nested-chain write path duplicated discriminators; the base-as-leaf read path parsed the document twice.

Fix

  • Add MIGRATION.md (Newtonsoft -> STJ and between STJ engines).
  • Rename FallBackSubTypeAttribute / FallBackToNearestAncestor in JsonSubTypes.Text.Json.
  • Fix duplicate discriminators in the AOT generator's nested-chain write path.
  • Eliminate the second JsonDocument parse on the base-as-leaf read path.
  • Prefix benchmark methods per scenario.

Tests

  • Full STJ + AOT + generator suites pass; the golden master is regenerated to match the generator change (net8.0 and net10.0).

Honest note(s)

  • The FallBack* rename is a breaking API change in the experimental STJ package; per the README the public API is not frozen before 1.0.0.

MIGRATION.md is an actionable before/after recipe: the mechanical renames, the
behaviors that actually differ between the Newtonsoft and STJ packages, and the
capability ceilings when moving between the converter, resolver and generator.
Also fix the outdated test count in the STJ status note and link the guide from
the README.
…SubTypes.Text.Json
The STJ package is still a release candidate, so now is the time to fix the
capitalization inherited from the Newtonsoft API: FallBackSubTypeAttribute ->
FallbackSubTypeAttribute and FallBackToNearestAncestor() ->
FallbackToNearestAncestor(). The Newtonsoft package keeps its historical names.
The generator matches attributes by namespace + short name, so it now looks up
FallbackSubTypeAttribute; the native JsonUnknownDerivedTypeHandling enum member
is untouched. All STJ (196), AOT (79) and Newtonsoft (153) tests pass.
…e path
When a nested hierarchy's discriminators are also native properties of the type
(e.g. [JsonPropertyName("$PayloadKind")]), the generated nested-chain writer
emitted every payload property without excluding the discriminator names, so the
injected discriminators were written twice. The runtime converter already
excluded them; the generator now skips any property whose name matches a
discriminator in the chain.
Adds a dedicated test fixture pinning the single-write and the round-trip to the
deepest subtype.
Converter_Serialize/Generated_Deserialize etc. collided across the single-object
and base-as-leaf classes, so a full-suite run produced ambiguous rows. Each class
now uses a unique scenario prefix (Single_, Col_, Nested_, Pres_, Leaf_, Nw_)
and PERFORMANCE.md documents the mapping and how to filter a scenario.
ReadObject parsed the JSON once to resolve the type, then ReadPlainObject parsed
it again from the reader to materialize the base object. Reuse the already-parsed
RootElement instead, matching how the subtype path deserializes.
Measured (BenchmarkDotNet, net10, DefaultJob): Leaf_Converter_Deserialize
1.58us / 560 B before, 1.16us / 360 B after. All STJ and AOT tests pass.
The nested-chain writer now skips properties whose name matches a
discriminator in the chain; the committed PayloadJsonSubTypesConverter
still carried the old output without the guard, failing the golden-master
test.
…ribute-based discriminator
The JsonSubTypeConverterAttribute kept passing a closed converter type to
JsonConverterAttribute, so System.Text.Json built the converter through its
parameterless constructor and the CreateConverter override was never called.
The attribute now leaves ConverterType null for the JsonSubtypes<T> forms and
routes through CreateConverter, which closes the generic over the annotated
type (new [JsonSubTypeConverter("Kind")] convenience constructors) and passes
the discriminator to the converter. As a result the attribute-based write path
now injects the discriminator for registered subtypes, matching the behaviour
the README and MIGRATION.md already documented. GetTypeResolver and
CreateTypeResolver close JsonSubtypes<> over the target type when the attribute
carries no converter type, so the resolver dance keeps working for nested
hierarchies.
Property-presence has no discriminator to inject, so serialization writes
the plain runtime contract. Add a parity test pinning that the runtime
converter and the generated converter emit the same output for a
property-presence subtype (all properties once, nothing injected), and
strengthen the attribute-based runtime test to assert the exact property
set so migrating never loses a property nor duplicates the discriminator.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@manuc66
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Migration guide, API renames and read-path fixes by manuc66 · Pull Request #210 · manuc66/JsonSubTypes · GitHub
Skip to content

Migration guide, API renames and read-path fixes - #210

Open
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes
Open

Migration guide, API renames and read-path fixes#210
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes

Conversation

@manuc66

Copy link
Copy Markdown
Owner

Problem

  • No migration guide existed for Newtonsoft -> STJ or between the STJ engines.
  • The STJ FallBack* names had inconsistent capitalization.
  • The AOT generator's nested-chain write path duplicated discriminators; the base-as-leaf read path parsed the document twice.

Fix

  • Add MIGRATION.md (Newtonsoft -> STJ and between STJ engines).
  • Rename FallBackSubTypeAttribute / FallBackToNearestAncestor in JsonSubTypes.Text.Json.
  • Fix duplicate discriminators in the AOT generator's nested-chain write path.
  • Eliminate the second JsonDocument parse on the base-as-leaf read path.
  • Prefix benchmark methods per scenario.

Tests

  • Full STJ + AOT + generator suites pass; the golden master is regenerated to match the generator change (net8.0 and net10.0).

Honest note(s)

  • The FallBack* rename is a breaking API change in the experimental STJ package; per the README the public API is not frozen before 1.0.0.

MIGRATION.md is an actionable before/after recipe: the mechanical renames, the
behaviors that actually differ between the Newtonsoft and STJ packages, and the
capability ceilings when moving between the converter, resolver and generator.
Also fix the outdated test count in the STJ status note and link the guide from
the README.
…SubTypes.Text.Json
The STJ package is still a release candidate, so now is the time to fix the
capitalization inherited from the Newtonsoft API: FallBackSubTypeAttribute ->
FallbackSubTypeAttribute and FallBackToNearestAncestor() ->
FallbackToNearestAncestor(). The Newtonsoft package keeps its historical names.
The generator matches attributes by namespace + short name, so it now looks up
FallbackSubTypeAttribute; the native JsonUnknownDerivedTypeHandling enum member
is untouched. All STJ (196), AOT (79) and Newtonsoft (153) tests pass.
…e path
When a nested hierarchy's discriminators are also native properties of the type
(e.g. [JsonPropertyName("$PayloadKind")]), the generated nested-chain writer
emitted every payload property without excluding the discriminator names, so the
injected discriminators were written twice. The runtime converter already
excluded them; the generator now skips any property whose name matches a
discriminator in the chain.
Adds a dedicated test fixture pinning the single-write and the round-trip to the
deepest subtype.
Converter_Serialize/Generated_Deserialize etc. collided across the single-object
and base-as-leaf classes, so a full-suite run produced ambiguous rows. Each class
now uses a unique scenario prefix (Single_, Col_, Nested_, Pres_, Leaf_, Nw_)
and PERFORMANCE.md documents the mapping and how to filter a scenario.
ReadObject parsed the JSON once to resolve the type, then ReadPlainObject parsed
it again from the reader to materialize the base object. Reuse the already-parsed
RootElement instead, matching how the subtype path deserializes.
Measured (BenchmarkDotNet, net10, DefaultJob): Leaf_Converter_Deserialize
1.58us / 560 B before, 1.16us / 360 B after. All STJ and AOT tests pass.
The nested-chain writer now skips properties whose name matches a
discriminator in the chain; the committed PayloadJsonSubTypesConverter
still carried the old output without the guard, failing the golden-master
test.
…ribute-based discriminator
The JsonSubTypeConverterAttribute kept passing a closed converter type to
JsonConverterAttribute, so System.Text.Json built the converter through its
parameterless constructor and the CreateConverter override was never called.
The attribute now leaves ConverterType null for the JsonSubtypes<T> forms and
routes through CreateConverter, which closes the generic over the annotated
type (new [JsonSubTypeConverter("Kind")] convenience constructors) and passes
the discriminator to the converter. As a result the attribute-based write path
now injects the discriminator for registered subtypes, matching the behaviour
the README and MIGRATION.md already documented. GetTypeResolver and
CreateTypeResolver close JsonSubtypes<> over the target type when the attribute
carries no converter type, so the resolver dance keeps working for nested
hierarchies.
Property-presence has no discriminator to inject, so serialization writes
the plain runtime contract. Add a parity test pinning that the runtime
converter and the generated converter emit the same output for a
property-presence subtype (all properties once, nothing injected), and
strengthen the attribute-based runtime test to assert the exact property
set so migrating never loses a property nor duplicates the discriminator.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@manuc66
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Migration guide, API renames and read-path fixes by manuc66 · Pull Request #210 · manuc66/JsonSubTypes · GitHub
Skip to content

Migration guide, API renames and read-path fixes - #210

Open
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes
Open

Migration guide, API renames and read-path fixes#210
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes

Conversation

@manuc66

Copy link
Copy Markdown
Owner

Problem

  • No migration guide existed for Newtonsoft -> STJ or between the STJ engines.
  • The STJ FallBack* names had inconsistent capitalization.
  • The AOT generator's nested-chain write path duplicated discriminators; the base-as-leaf read path parsed the document twice.

Fix

  • Add MIGRATION.md (Newtonsoft -> STJ and between STJ engines).
  • Rename FallBackSubTypeAttribute / FallBackToNearestAncestor in JsonSubTypes.Text.Json.
  • Fix duplicate discriminators in the AOT generator's nested-chain write path.
  • Eliminate the second JsonDocument parse on the base-as-leaf read path.
  • Prefix benchmark methods per scenario.

Tests

  • Full STJ + AOT + generator suites pass; the golden master is regenerated to match the generator change (net8.0 and net10.0).

Honest note(s)

  • The FallBack* rename is a breaking API change in the experimental STJ package; per the README the public API is not frozen before 1.0.0.

MIGRATION.md is an actionable before/after recipe: the mechanical renames, the
behaviors that actually differ between the Newtonsoft and STJ packages, and the
capability ceilings when moving between the converter, resolver and generator.
Also fix the outdated test count in the STJ status note and link the guide from
the README.
…SubTypes.Text.Json
The STJ package is still a release candidate, so now is the time to fix the
capitalization inherited from the Newtonsoft API: FallBackSubTypeAttribute ->
FallbackSubTypeAttribute and FallBackToNearestAncestor() ->
FallbackToNearestAncestor(). The Newtonsoft package keeps its historical names.
The generator matches attributes by namespace + short name, so it now looks up
FallbackSubTypeAttribute; the native JsonUnknownDerivedTypeHandling enum member
is untouched. All STJ (196), AOT (79) and Newtonsoft (153) tests pass.
…e path
When a nested hierarchy's discriminators are also native properties of the type
(e.g. [JsonPropertyName("$PayloadKind")]), the generated nested-chain writer
emitted every payload property without excluding the discriminator names, so the
injected discriminators were written twice. The runtime converter already
excluded them; the generator now skips any property whose name matches a
discriminator in the chain.
Adds a dedicated test fixture pinning the single-write and the round-trip to the
deepest subtype.
Converter_Serialize/Generated_Deserialize etc. collided across the single-object
and base-as-leaf classes, so a full-suite run produced ambiguous rows. Each class
now uses a unique scenario prefix (Single_, Col_, Nested_, Pres_, Leaf_, Nw_)
and PERFORMANCE.md documents the mapping and how to filter a scenario.
ReadObject parsed the JSON once to resolve the type, then ReadPlainObject parsed
it again from the reader to materialize the base object. Reuse the already-parsed
RootElement instead, matching how the subtype path deserializes.
Measured (BenchmarkDotNet, net10, DefaultJob): Leaf_Converter_Deserialize
1.58us / 560 B before, 1.16us / 360 B after. All STJ and AOT tests pass.
The nested-chain writer now skips properties whose name matches a
discriminator in the chain; the committed PayloadJsonSubTypesConverter
still carried the old output without the guard, failing the golden-master
test.
…ribute-based discriminator
The JsonSubTypeConverterAttribute kept passing a closed converter type to
JsonConverterAttribute, so System.Text.Json built the converter through its
parameterless constructor and the CreateConverter override was never called.
The attribute now leaves ConverterType null for the JsonSubtypes<T> forms and
routes through CreateConverter, which closes the generic over the annotated
type (new [JsonSubTypeConverter("Kind")] convenience constructors) and passes
the discriminator to the converter. As a result the attribute-based write path
now injects the discriminator for registered subtypes, matching the behaviour
the README and MIGRATION.md already documented. GetTypeResolver and
CreateTypeResolver close JsonSubtypes<> over the target type when the attribute
carries no converter type, so the resolver dance keeps working for nested
hierarchies.
Property-presence has no discriminator to inject, so serialization writes
the plain runtime contract. Add a parity test pinning that the runtime
converter and the generated converter emit the same output for a
property-presence subtype (all properties once, nothing injected), and
strengthen the attribute-based runtime test to assert the exact property
set so migrating never loses a property nor duplicates the discriminator.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@manuc66
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Migration guide, API renames and read-path fixes by manuc66 · Pull Request #210 · manuc66/JsonSubTypes · GitHub
Skip to content

Migration guide, API renames and read-path fixes - #210

Open
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes
Open

Migration guide, API renames and read-path fixes#210
manuc66 wants to merge 8 commits into
feature/split/pr3-newtonsoft-perffrom
feature/split/pr4-migration-fixes

Conversation

@manuc66

Copy link
Copy Markdown
Owner

Problem

  • No migration guide existed for Newtonsoft -> STJ or between the STJ engines.
  • The STJ FallBack* names had inconsistent capitalization.
  • The AOT generator's nested-chain write path duplicated discriminators; the base-as-leaf read path parsed the document twice.

Fix

  • Add MIGRATION.md (Newtonsoft -> STJ and between STJ engines).
  • Rename FallBackSubTypeAttribute / FallBackToNearestAncestor in JsonSubTypes.Text.Json.
  • Fix duplicate discriminators in the AOT generator's nested-chain write path.
  • Eliminate the second JsonDocument parse on the base-as-leaf read path.
  • Prefix benchmark methods per scenario.

Tests

  • Full STJ + AOT + generator suites pass; the golden master is regenerated to match the generator change (net8.0 and net10.0).

Honest note(s)

  • The FallBack* rename is a breaking API change in the experimental STJ package; per the README the public API is not frozen before 1.0.0.

MIGRATION.md is an actionable before/after recipe: the mechanical renames, the
behaviors that actually differ between the Newtonsoft and STJ packages, and the
capability ceilings when moving between the converter, resolver and generator.
Also fix the outdated test count in the STJ status note and link the guide from
the README.
…SubTypes.Text.Json
The STJ package is still a release candidate, so now is the time to fix the
capitalization inherited from the Newtonsoft API: FallBackSubTypeAttribute ->
FallbackSubTypeAttribute and FallBackToNearestAncestor() ->
FallbackToNearestAncestor(). The Newtonsoft package keeps its historical names.
The generator matches attributes by namespace + short name, so it now looks up
FallbackSubTypeAttribute; the native JsonUnknownDerivedTypeHandling enum member
is untouched. All STJ (196), AOT (79) and Newtonsoft (153) tests pass.
…e path
When a nested hierarchy's discriminators are also native properties of the type
(e.g. [JsonPropertyName("$PayloadKind")]), the generated nested-chain writer
emitted every payload property without excluding the discriminator names, so the
injected discriminators were written twice. The runtime converter already
excluded them; the generator now skips any property whose name matches a
discriminator in the chain.
Adds a dedicated test fixture pinning the single-write and the round-trip to the
deepest subtype.
Converter_Serialize/Generated_Deserialize etc. collided across the single-object
and base-as-leaf classes, so a full-suite run produced ambiguous rows. Each class
now uses a unique scenario prefix (Single_, Col_, Nested_, Pres_, Leaf_, Nw_)
and PERFORMANCE.md documents the mapping and how to filter a scenario.
ReadObject parsed the JSON once to resolve the type, then ReadPlainObject parsed
it again from the reader to materialize the base object. Reuse the already-parsed
RootElement instead, matching how the subtype path deserializes.
Measured (BenchmarkDotNet, net10, DefaultJob): Leaf_Converter_Deserialize
1.58us / 560 B before, 1.16us / 360 B after. All STJ and AOT tests pass.
The nested-chain writer now skips properties whose name matches a
discriminator in the chain; the committed PayloadJsonSubTypesConverter
still carried the old output without the guard, failing the golden-master
test.
…ribute-based discriminator
The JsonSubTypeConverterAttribute kept passing a closed converter type to
JsonConverterAttribute, so System.Text.Json built the converter through its
parameterless constructor and the CreateConverter override was never called.
The attribute now leaves ConverterType null for the JsonSubtypes<T> forms and
routes through CreateConverter, which closes the generic over the annotated
type (new [JsonSubTypeConverter("Kind")] convenience constructors) and passes
the discriminator to the converter. As a result the attribute-based write path
now injects the discriminator for registered subtypes, matching the behaviour
the README and MIGRATION.md already documented. GetTypeResolver and
CreateTypeResolver close JsonSubtypes<> over the target type when the attribute
carries no converter type, so the resolver dance keeps working for nested
hierarchies.
Property-presence has no discriminator to inject, so serialization writes
the plain runtime contract. Add a parity test pinning that the runtime
converter and the generated converter emit the same output for a
property-presence subtype (all properties once, nothing injected), and
strengthen the attribute-based runtime test to assert the exact property
set so migrating never loses a property nor duplicates the discriminator.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@manuc66