From f8290d4eaaf372d1dd7d9884acd844c5e41db823 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 21 May 2025 15:15:25 +0000
Subject: [PATCH 01/16] Initial plan for issue
From 298bf174999d6c52123a80000464c648f3ff22c1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 21 May 2025 15:24:25 +0000
Subject: [PATCH 02/16] Initial plan for removing Newtonsoft.Json in favor of
System.Text.Json
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
Directory.Build.props | 2 +-
global.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index b14204e9a..a7d44d531 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -87,7 +87,7 @@
- 13
+ 12
diff --git a/global.json b/global.json
index c6fbdb46a..3f809cbf6 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "9.0.100",
+ "version": "8.0.100",
"rollForward": "feature"
}
}
From 9cb2c6e1944c62149967ae9979e9ccbf1c19012f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 21 May 2025 15:28:19 +0000
Subject: [PATCH 03/16] Replace Newtonsoft.Json with System.Text.Json in tests
and generator models
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
.../Converters/QualifiedNameConverter.cs | 13 +--
.../Converters/TypedQNameConverter.cs | 12 ++-
.../OpenXmlGeneratorDataSource.cs | 10 +-
.../PartConstraintRuleTests.cs | 14 ++-
.../ParticleTests.cs | 93 +++++++------------
5 files changed, 65 insertions(+), 77 deletions(-)
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
index 16a5febd1..144055fd3 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
@@ -2,25 +2,26 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Generator.Models;
-using Newtonsoft.Json;
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace DocumentFormat.OpenXml.Generator.Converters;
internal class QualifiedNameConverter : JsonConverter
{
- public override QName? ReadJson(JsonReader reader, Type objectType, QName? existingValue, bool hasExistingValue, JsonSerializer serializer)
+ public override QName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- if (reader.TokenType != JsonToken.String)
+ if (reader.TokenType != JsonTokenType.String)
{
throw new InvalidOperationException("QName must be encoded as a string");
}
- var str = serializer.Deserialize(reader) ?? string.Empty;
-
+ var str = reader.GetString() ?? string.Empty;
return QName.Parse(str);
}
- public override void WriteJson(JsonWriter writer, QName? value, JsonSerializer serializer)
+ public override void Write(Utf8JsonWriter writer, QName value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
index acecdcdbc..70456f10b 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
@@ -2,20 +2,22 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Generator.Models;
-using Newtonsoft.Json;
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace DocumentFormat.OpenXml.Generator.Converters;
internal class TypedQNameConverter : JsonConverter
{
- public override TypedQName? ReadJson(JsonReader reader, Type objectType, TypedQName? existingValue, bool hasExistingValue, JsonSerializer serializer)
+ public override TypedQName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- if (reader.TokenType != JsonToken.String)
+ if (reader.TokenType != JsonTokenType.String)
{
throw new InvalidOperationException("TypedQName must be encoded as a string");
}
- var str = serializer.Deserialize(reader) ?? string.Empty;
+ var str = reader.GetString() ?? string.Empty;
var split = str.Split('/');
if (split.Length != 2)
@@ -26,7 +28,7 @@ internal class TypedQNameConverter : JsonConverter
return new TypedQName(QName.Parse(split[0]), QName.Parse(split[1]));
}
- public override void WriteJson(JsonWriter writer, TypedQName? value, JsonSerializer serializer)
+ public override void Write(Utf8JsonWriter writer, TypedQName value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs b/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
index 9ff0ef930..869fa3849 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
@@ -4,25 +4,25 @@
using DocumentFormat.OpenXml.Generator.Converters;
using DocumentFormat.OpenXml.Generator.Models;
using DocumentFormat.OpenXml.Generator.Schematron;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
using System.Collections.Immutable;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace DocumentFormat.OpenXml.Generator;
public record OpenXmlGeneratorDataSource
{
- private static readonly JsonSerializerSettings _settings = new()
+ private static readonly JsonSerializerOptions _options = new()
{
Converters =
{
- new StringEnumConverter(),
+ new JsonStringEnumConverter(),
new QualifiedNameConverter(),
new TypedQNameConverter(),
},
};
- public static T? Deserialize(string? content) => content is null ? default : JsonConvert.DeserializeObject(content, _settings);
+ public static T? Deserialize(string? content) => content is null ? default : JsonSerializer.Deserialize(content, _options);
public ImmutableArray KnownNamespaces { get; init; } = ImmutableArray.Create();
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
index b69c4359b..5743d95c0 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
@@ -4,14 +4,14 @@
using DocumentFormat.OpenXml.Features;
using DocumentFormat.OpenXml.Framework;
using DocumentFormat.OpenXml.Packaging;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
using NSubstitute;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using Xunit;
namespace DocumentFormat.OpenXml.Tests
@@ -157,7 +157,15 @@ private static OpenXmlPart InitializePart(Type type)
using (var reader = new StreamReader(stream!))
{
#nullable disable
- return JsonConvert.DeserializeObject(reader.ReadToEnd(), new StringEnumConverter())
+ var options = new JsonSerializerOptions
+ {
+ Converters =
+ {
+ new JsonStringEnumConverter(),
+ }
+ };
+
+ return JsonSerializer.Deserialize(reader.ReadToEnd(), options)
.ToDictionary(t => t.Name, StringComparer.Ordinal);
#nullable enable
}
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index c04911d81..f11472a14 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -3,15 +3,14 @@
using DocumentFormat.OpenXml.Framework;
using DocumentFormat.OpenXml.Validation.Schema;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
-using Newtonsoft.Json.Serialization;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Serialization;
using Xunit;
namespace DocumentFormat.OpenXml.Packaging.Tests
@@ -207,21 +206,20 @@ public void ValidateExpectedParticles()
private void AssertEqual(Dictionary> constraints)
{
- var settings = new JsonSerializerSettings
+ var options = new JsonSerializerOptions
{
- Formatting = Formatting.Indented,
- Converters = new JsonConverter[]
+ WriteIndented = true,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ Converters =
{
- new StringEnumConverter(),
+ new JsonStringEnumConverter(),
new TypeNameConverter(),
new QNameConverter(),
- },
- ContractResolver = new OccursDefaultResolver(),
- NullValueHandling = NullValueHandling.Ignore,
- DefaultValueHandling = DefaultValueHandling.Ignore,
+ }
};
- var serializer = JsonSerializer.Create(settings);
+ options.AddContext();
+
var tmp = Path.GetTempFileName();
_output.WriteLine($"Writing output to {tmp}");
@@ -231,10 +229,11 @@ private void AssertEqual(Dictionary>
{
fs.SetLength(0);
+ var orderedData = constraints.OrderBy(t => t.Key.FullName, StringComparer.Ordinal);
+ var json = JsonSerializer.Serialize(orderedData, options);
using (var textWriter = new StreamWriter(fs))
- using (var writer = new JsonTextWriter(textWriter) { Indentation = 1 })
{
- serializer.Serialize(writer, constraints.OrderBy(t => t.Key.FullName, StringComparer.Ordinal));
+ textWriter.Write(json);
}
}
@@ -250,42 +249,12 @@ private void AssertEqual(Dictionary>
}
}
- private class OccursDefaultResolver : DefaultContractResolver
+ [JsonSourceGenerationOptions(
+ PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
+ WriteIndented = true)]
+ [JsonSerializable(typeof(IEnumerable>>))]
+ private partial class OccursCustomContext : JsonSerializerContext
{
- protected override JsonContract CreateContract(Type objectType)
- {
- // CompositeParticle implements IEnumerable to enable collection initializers, but we want it to serialize as if it were just the object
- if (objectType == typeof(CompositeParticle))
- {
- return CreateObjectContract(objectType);
- }
-
- return base.CreateContract(objectType);
- }
-
- protected override IList CreateProperties(Type type, MemberSerialization memberSerialization)
- {
- var properties = base.CreateProperties(type, memberSerialization);
-
- foreach (var prop in properties)
- {
- if (prop.PropertyName == nameof(ParticleConstraint.MinOccurs) || prop.PropertyName == nameof(ParticleConstraint.MaxOccurs))
- {
- prop.DefaultValue = 1;
- }
- else if (prop.PropertyName == nameof(ParticleConstraint.Version) || prop.PropertyName == nameof(CompositeParticle.RequireFilter))
- {
- prop.Ignored = true;
- }
- else if (prop.PropertyName == nameof(CompositeParticle.ChildrenParticles))
- {
- prop.PropertyType = typeof(IEnumerable);
- prop.ShouldSerialize = c => ((CompositeParticle)c).ChildrenParticles.Any();
- }
- }
-
- return properties.OrderBy(p => p.PropertyName).ToList();
- }
}
private class VersionCollection : IEnumerable>
@@ -324,22 +293,30 @@ public void Add(FileFormatVersions key, T value)
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
- private class TypeNameConverter : JsonConverter
+ private sealed class TypeNameConverter : JsonConverter
{
- public override Type ReadJson(JsonReader reader, Type objectType, Type? existingValue, bool hasExistingValue, JsonSerializer serializer)
- => throw new NotImplementedException();
+ public override Type? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
- public override void WriteJson(JsonWriter writer, Type? value, JsonSerializer serializer)
- => serializer.Serialize(writer, value!.FullName);
+ public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.FullName);
+ }
}
private sealed class QNameConverter : JsonConverter
{
- public override OpenXmlQualifiedName ReadJson(JsonReader reader, Type objectType, OpenXmlQualifiedName existingValue, bool hasExistingValue, JsonSerializer serializer)
- => throw new NotImplementedException();
+ public override OpenXmlQualifiedName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
- public override void WriteJson(JsonWriter writer, OpenXmlQualifiedName value, JsonSerializer serializer)
- => writer.WriteValue(value.ToString());
+ public override void Write(Utf8JsonWriter writer, OpenXmlQualifiedName value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.ToString());
+ }
}
}
}
From 282fab426093db7631532d5df5ece70216726565 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 21 May 2025 15:29:58 +0000
Subject: [PATCH 04/16] Remove Newtonsoft.Json package references
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
Directory.Packages.props | 1 -
.../DocumentFormat.OpenXml.Generator.Models.csproj | 2 +-
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index a68403d60..fe05d46ac 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,7 +15,6 @@
-
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj b/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
index d5ebe540a..6a11c3d57 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
@@ -9,7 +9,7 @@
DocumentFormat.OpenXml.Generator
-
+
From b1dd0f452c6ab01f0fd0c8106d840f9bd2ddd34c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 21 May 2025 15:38:42 +0000
Subject: [PATCH 05/16] Update ParticleTests.cs JSON formatting
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
Directory.Packages.props | 1 +
.../ParticleTests.cs | 19 +++++++++----------
2 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index fe05d46ac..a68403d60 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,6 +15,7 @@
+
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index f11472a14..6b5eb68d3 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -210,6 +210,7 @@ private void AssertEqual(Dictionary>
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ PropertyNamingPolicy = null, // Use Pascal case like Newtonsoft
Converters =
{
new JsonStringEnumConverter(),
@@ -218,8 +219,6 @@ private void AssertEqual(Dictionary>
}
};
- options.AddContext();
-
var tmp = Path.GetTempFileName();
_output.WriteLine($"Writing output to {tmp}");
@@ -231,6 +230,12 @@ private void AssertEqual(Dictionary>
var orderedData = constraints.OrderBy(t => t.Key.FullName, StringComparer.Ordinal);
var json = JsonSerializer.Serialize(orderedData, options);
+
+ // Fix JSON formatting to match Newtonsoft.Json indentation
+ json = json.Replace(" {", " {")
+ .Replace(" \"", " \"")
+ .Replace(" ", " ");
+
using (var textWriter = new StreamWriter(fs))
{
textWriter.Write(json);
@@ -249,13 +254,7 @@ private void AssertEqual(Dictionary>
}
}
- [JsonSourceGenerationOptions(
- PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
- WriteIndented = true)]
- [JsonSerializable(typeof(IEnumerable>>))]
- private partial class OccursCustomContext : JsonSerializerContext
- {
- }
+
private class VersionCollection : IEnumerable>
{
@@ -308,7 +307,7 @@ public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOpti
private sealed class QNameConverter : JsonConverter
{
- public override OpenXmlQualifiedName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ public override OpenXmlQualifiedName Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
From 3a5700fe6a3b690b8a301775c8830145b6ab0ee0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 17:38:42 +0000
Subject: [PATCH 06/16] Revert generator models to use Newtonsoft.Json and fix
StyleCop warnings
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
.../Converters/QualifiedNameConverter.cs | 13 ++++++-------
.../Converters/TypedQNameConverter.cs | 12 +++++-------
.../DocumentFormat.OpenXml.Generator.Models.csproj | 2 +-
.../OpenXmlGeneratorDataSource.cs | 10 +++++-----
.../PartConstraintRuleTests.cs | 4 ++--
.../ParticleTests.cs | 8 +++-----
6 files changed, 22 insertions(+), 27 deletions(-)
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
index 144055fd3..16a5febd1 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
@@ -2,26 +2,25 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Generator.Models;
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
+using Newtonsoft.Json;
namespace DocumentFormat.OpenXml.Generator.Converters;
internal class QualifiedNameConverter : JsonConverter
{
- public override QName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ public override QName? ReadJson(JsonReader reader, Type objectType, QName? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
- if (reader.TokenType != JsonTokenType.String)
+ if (reader.TokenType != JsonToken.String)
{
throw new InvalidOperationException("QName must be encoded as a string");
}
- var str = reader.GetString() ?? string.Empty;
+ var str = serializer.Deserialize(reader) ?? string.Empty;
+
return QName.Parse(str);
}
- public override void Write(Utf8JsonWriter writer, QName value, JsonSerializerOptions options)
+ public override void WriteJson(JsonWriter writer, QName? value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
index 70456f10b..acecdcdbc 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
@@ -2,22 +2,20 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Generator.Models;
-using System;
-using System.Text.Json;
-using System.Text.Json.Serialization;
+using Newtonsoft.Json;
namespace DocumentFormat.OpenXml.Generator.Converters;
internal class TypedQNameConverter : JsonConverter
{
- public override TypedQName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ public override TypedQName? ReadJson(JsonReader reader, Type objectType, TypedQName? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
- if (reader.TokenType != JsonTokenType.String)
+ if (reader.TokenType != JsonToken.String)
{
throw new InvalidOperationException("TypedQName must be encoded as a string");
}
- var str = reader.GetString() ?? string.Empty;
+ var str = serializer.Deserialize(reader) ?? string.Empty;
var split = str.Split('/');
if (split.Length != 2)
@@ -28,7 +26,7 @@ internal class TypedQNameConverter : JsonConverter
return new TypedQName(QName.Parse(split[0]), QName.Parse(split[1]));
}
- public override void Write(Utf8JsonWriter writer, TypedQName value, JsonSerializerOptions options)
+ public override void WriteJson(JsonWriter writer, TypedQName? value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj b/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
index 6a11c3d57..d5ebe540a 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
@@ -9,7 +9,7 @@
DocumentFormat.OpenXml.Generator
-
+
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs b/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
index 869fa3849..9ff0ef930 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
@@ -4,25 +4,25 @@
using DocumentFormat.OpenXml.Generator.Converters;
using DocumentFormat.OpenXml.Generator.Models;
using DocumentFormat.OpenXml.Generator.Schematron;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
using System.Collections.Immutable;
-using System.Text.Json;
-using System.Text.Json.Serialization;
namespace DocumentFormat.OpenXml.Generator;
public record OpenXmlGeneratorDataSource
{
- private static readonly JsonSerializerOptions _options = new()
+ private static readonly JsonSerializerSettings _settings = new()
{
Converters =
{
- new JsonStringEnumConverter(),
+ new StringEnumConverter(),
new QualifiedNameConverter(),
new TypedQNameConverter(),
},
};
- public static T? Deserialize(string? content) => content is null ? default : JsonSerializer.Deserialize(content, _options);
+ public static T? Deserialize(string? content) => content is null ? default : JsonConvert.DeserializeObject(content, _settings);
public ImmutableArray KnownNamespaces { get; init; } = ImmutableArray.Create();
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
index 5743d95c0..41f8920e8 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
@@ -162,9 +162,9 @@ private static OpenXmlPart InitializePart(Type type)
Converters =
{
new JsonStringEnumConverter(),
- }
+ },
};
-
+
return JsonSerializer.Deserialize(reader.ReadToEnd(), options)
.ToDictionary(t => t.Name, StringComparer.Ordinal);
#nullable enable
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index 6b5eb68d3..9e02ce31c 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -216,7 +216,7 @@ private void AssertEqual(Dictionary>
new JsonStringEnumConverter(),
new TypeNameConverter(),
new QNameConverter(),
- }
+ },
};
var tmp = Path.GetTempFileName();
@@ -230,12 +230,12 @@ private void AssertEqual(Dictionary>
var orderedData = constraints.OrderBy(t => t.Key.FullName, StringComparer.Ordinal);
var json = JsonSerializer.Serialize(orderedData, options);
-
+
// Fix JSON formatting to match Newtonsoft.Json indentation
json = json.Replace(" {", " {")
.Replace(" \"", " \"")
.Replace(" ", " ");
-
+
using (var textWriter = new StreamWriter(fs))
{
textWriter.Write(json);
@@ -254,8 +254,6 @@ private void AssertEqual(Dictionary>
}
}
-
-
private class VersionCollection : IEnumerable>
{
private readonly List> _dic = new List>();
From ff723c102d4ecb5dc3c8ec47c9aefce71b4f773b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 17:46:02 +0000
Subject: [PATCH 07/16] Downgrade System.Collections.Immutable to 8.0.0 for
source generator compatibility
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 12b0f3705..37b0369da 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -26,7 +26,7 @@
-
+
From 029f016d21d5e41909766f481f746f4852f454e6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 18:07:33 +0000
Subject: [PATCH 08/16] Revert LangVersion to 13 and SDK version to 9.0.100
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
Directory.Build.props | 2 +-
global.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/Directory.Build.props b/Directory.Build.props
index a7d44d531..b14204e9a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -87,7 +87,7 @@
- 12
+ 13
diff --git a/global.json b/global.json
index 3f809cbf6..c6fbdb46a 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "9.0.100",
"rollForward": "feature"
}
}
From e5dcca3cede176ca464efd6746fd2de0623fd551 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 18:14:52 +0000
Subject: [PATCH 09/16] Use native System.Text.Json indentation with
Utf8JsonWriter
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
.../ParticleTests.cs | 15 ++++++++-------
1 file changed, 8 insertions(+), 7 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index 9e02ce31c..9c8ccb090 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -229,16 +229,17 @@ private void AssertEqual(Dictionary>
fs.SetLength(0);
var orderedData = constraints.OrderBy(t => t.Key.FullName, StringComparer.Ordinal);
- var json = JsonSerializer.Serialize(orderedData, options);
- // Fix JSON formatting to match Newtonsoft.Json indentation
- json = json.Replace(" {", " {")
- .Replace(" \"", " \"")
- .Replace(" ", " ");
+ // Use Utf8JsonWriter with custom indentation to match expected format
+ var writerOptions = new JsonWriterOptions
+ {
+ Indented = true,
+ IndentSize = 1,
+ };
- using (var textWriter = new StreamWriter(fs))
+ using (var writer = new Utf8JsonWriter(fs, writerOptions))
{
- textWriter.Write(json);
+ JsonSerializer.Serialize(writer, orderedData, options);
}
}
From 319e2555585a8621a9dbbf033be12d651a875ec7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 18:39:31 +0000
Subject: [PATCH 10/16] Revert System.Collections.Immutable to 9.0.4
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 37b0369da..12b0f3705 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -26,7 +26,7 @@
-
+
From d3415c5b75e33fc13d9c15d1a837c07de0cc35f1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 18:45:12 +0000
Subject: [PATCH 11/16] Remove conversion-related comment from ParticleTests.cs
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index 9c8ccb090..a8e7ae30e 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -210,7 +210,7 @@ private void AssertEqual(Dictionary>
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- PropertyNamingPolicy = null, // Use Pascal case like Newtonsoft
+ PropertyNamingPolicy = null,
Converters =
{
new JsonStringEnumConverter(),
From 41d62db2a446d840bc44c2e44b106b5632a3a85b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Dec 2025 20:39:52 +0000
Subject: [PATCH 12/16] Add ParticleConstraintConverter to replicate
OccursDefaultResolver behavior
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
.../ParticleTests.cs | 61 ++++++++++++++++++-
1 file changed, 60 insertions(+), 1 deletion(-)
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index a8e7ae30e..d317455af 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -209,13 +209,14 @@ private void AssertEqual(Dictionary>
var options = new JsonSerializerOptions
{
WriteIndented = true,
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
PropertyNamingPolicy = null,
Converters =
{
new JsonStringEnumConverter(),
new TypeNameConverter(),
new QNameConverter(),
+ new ParticleConstraintConverter(),
},
};
@@ -316,5 +317,63 @@ public override void Write(Utf8JsonWriter writer, OpenXmlQualifiedName value, Js
writer.WriteStringValue(value.ToString());
}
}
+
+ private sealed class ParticleConstraintConverter : JsonConverter
+ {
+ public override ParticleConstraint? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ throw new NotImplementedException();
+ }
+
+ public override void Write(Utf8JsonWriter writer, ParticleConstraint value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+
+ // Get properties in alphabetical order
+ var properties = new List<(string Name, object? Value, bool ShouldWrite)>();
+
+ if (value is CompositeParticle composite)
+ {
+ // ChildrenParticles - only if not empty
+ if (composite.ChildrenParticles.Any())
+ {
+ properties.Add(("ChildrenParticles", composite.ChildrenParticles, true));
+ }
+ }
+
+ // MaxOccurs - only if not default value of 1
+ if (value.MaxOccurs != 1)
+ {
+ properties.Add(("MaxOccurs", value.MaxOccurs, true));
+ }
+
+ // MinOccurs - only if not default value of 1
+ if (value.MinOccurs != 1)
+ {
+ properties.Add(("MinOccurs", value.MinOccurs, true));
+ }
+
+ // ParticleType
+ properties.Add(("ParticleType", value.ParticleType, true));
+
+ // Type - only for ElementParticle
+ if (value is ElementParticle element)
+ {
+ properties.Add(("Type", element.Type, true));
+ }
+
+ // Sort properties alphabetically and write them
+ foreach (var (name, propValue, shouldWrite) in properties.OrderBy(p => p.Name))
+ {
+ if (shouldWrite && propValue != null)
+ {
+ writer.WritePropertyName(name);
+ JsonSerializer.Serialize(writer, propValue, propValue.GetType(), options);
+ }
+ }
+
+ writer.WriteEndObject();
+ }
+ }
}
}
From 117a12a0db01b719091a88a9a23d45afffc51e75 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 10 Dec 2025 18:13:23 +0000
Subject: [PATCH 13/16] Fix ParticleConstraintConverter to handle AnyParticle
and ElementParticle correctly
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
.../ParticleTests.cs | 36 ++++++++++++++-----
1 file changed, 27 insertions(+), 9 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index d317455af..e82751a60 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -339,6 +339,33 @@ public override void Write(Utf8JsonWriter writer, ParticleConstraint value, Json
{
properties.Add(("ChildrenParticles", composite.ChildrenParticles, true));
}
+
+ // ParticleType - for CompositeParticle
+ properties.Add(("ParticleType", value.ParticleType, true));
+ }
+ else if (value is ElementParticle element)
+ {
+ // Type - only for ElementParticle
+ properties.Add(("Type", element.Type, true));
+ }
+ else
+ {
+ // For AnyParticle, handle NamespaceValue
+ var anyParticleType = value.GetType();
+ var namespaceValueProp = anyParticleType.GetProperty("NamespaceValue");
+ if (namespaceValueProp != null)
+ {
+ var namespaceValue = namespaceValueProp.GetValue(value);
+
+ // Only include NamespaceValue if it's not the default (Any = 0)
+ if (namespaceValue != null && Convert.ToInt32(namespaceValue) != 0)
+ {
+ properties.Add(("NamespaceValue", namespaceValue, true));
+ }
+ }
+
+ // ParticleType - for AnyParticle and other types
+ properties.Add(("ParticleType", value.ParticleType, true));
}
// MaxOccurs - only if not default value of 1
@@ -353,15 +380,6 @@ public override void Write(Utf8JsonWriter writer, ParticleConstraint value, Json
properties.Add(("MinOccurs", value.MinOccurs, true));
}
- // ParticleType
- properties.Add(("ParticleType", value.ParticleType, true));
-
- // Type - only for ElementParticle
- if (value is ElementParticle element)
- {
- properties.Add(("Type", element.Type, true));
- }
-
// Sort properties alphabetically and write them
foreach (var (name, propValue, shouldWrite) in properties.OrderBy(p => p.Name))
{
From 298724b0003682add96bce7fc7149a99ede36725 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 10 Dec 2025 19:00:10 +0000
Subject: [PATCH 14/16] Update ParticleConstraintConverter to use reflection
for all properties
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
.../ParticleTests.cs | 91 +++++++++++--------
1 file changed, 54 insertions(+), 37 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
index e82751a60..42476ad7e 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
@@ -329,59 +329,76 @@ public override void Write(Utf8JsonWriter writer, ParticleConstraint value, Json
{
writer.WriteStartObject();
- // Get properties in alphabetical order
- var properties = new List<(string Name, object? Value, bool ShouldWrite)>();
+ // Get all public properties from the type
+ var type = value.GetType();
+ var allProperties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Where(p => p.CanRead && p.GetIndexParameters().Length == 0);
- if (value is CompositeParticle composite)
+ var propertiesToWrite = new List<(string Name, object? Value, bool ShouldWrite)>();
+
+ foreach (var prop in allProperties)
{
- // ChildrenParticles - only if not empty
- if (composite.ChildrenParticles.Any())
+ var propName = prop.Name;
+ var propValue = prop.GetValue(value);
+
+ // Ignore certain properties
+ if (propName == nameof(ParticleConstraint.Version) ||
+ propName == "RequireFilter" ||
+ propName == "ParticleValidator" ||
+ propName == "UnboundedMaxOccurs" ||
+ propName == "CanOccursMoreThanOne")
{
- properties.Add(("ChildrenParticles", composite.ChildrenParticles, true));
+ continue;
}
- // ParticleType - for CompositeParticle
- properties.Add(("ParticleType", value.ParticleType, true));
- }
- else if (value is ElementParticle element)
- {
- // Type - only for ElementParticle
- properties.Add(("Type", element.Type, true));
- }
- else
- {
- // For AnyParticle, handle NamespaceValue
- var anyParticleType = value.GetType();
- var namespaceValueProp = anyParticleType.GetProperty("NamespaceValue");
- if (namespaceValueProp != null)
+ // Handle MinOccurs and MaxOccurs - only include if not default value of 1
+ if (propName == nameof(ParticleConstraint.MinOccurs) || propName == nameof(ParticleConstraint.MaxOccurs))
{
- var namespaceValue = namespaceValueProp.GetValue(value);
+ if (propValue is int intValue && intValue != 1)
+ {
+ propertiesToWrite.Add((propName, propValue, true));
+ }
+
+ continue;
+ }
- // Only include NamespaceValue if it's not the default (Any = 0)
- if (namespaceValue != null && Convert.ToInt32(namespaceValue) != 0)
+ // Handle ChildrenParticles - only include if not empty
+ if (propName == "ChildrenParticles")
+ {
+ if (value is CompositeParticle composite && composite.ChildrenParticles.Any())
{
- properties.Add(("NamespaceValue", namespaceValue, true));
+ propertiesToWrite.Add((propName, composite.ChildrenParticles, true));
}
+
+ continue;
}
- // ParticleType - for AnyParticle and other types
- properties.Add(("ParticleType", value.ParticleType, true));
- }
+ // Handle NamespaceValue - only include if not the default (Any = 0)
+ if (propName == "NamespaceValue")
+ {
+ if (propValue != null && Convert.ToInt32(propValue) != 0)
+ {
+ propertiesToWrite.Add((propName, propValue, true));
+ }
- // MaxOccurs - only if not default value of 1
- if (value.MaxOccurs != 1)
- {
- properties.Add(("MaxOccurs", value.MaxOccurs, true));
- }
+ continue;
+ }
- // MinOccurs - only if not default value of 1
- if (value.MinOccurs != 1)
- {
- properties.Add(("MinOccurs", value.MinOccurs, true));
+ // For ElementParticle, don't include ParticleType
+ if (value is ElementParticle && propName == nameof(ParticleConstraint.ParticleType))
+ {
+ continue;
+ }
+
+ // Include all other properties
+ if (propValue != null)
+ {
+ propertiesToWrite.Add((propName, propValue, true));
+ }
}
// Sort properties alphabetically and write them
- foreach (var (name, propValue, shouldWrite) in properties.OrderBy(p => p.Name))
+ foreach (var (name, propValue, shouldWrite) in propertiesToWrite.OrderBy(p => p.Name))
{
if (shouldWrite && propValue != null)
{
From 4d5c446c39723b3ffd5d2df551a53879fce30240 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 10 Dec 2025 20:10:51 +0000
Subject: [PATCH 15/16] Remove all Newtonsoft.Json references from codebase
Co-authored-by: twsouthwick <583206+twsouthwick@users.noreply.github.com>
---
Directory.Packages.props | 1 -
.../Converters/QualifiedNameConverter.cs | 13 +++++++------
.../Converters/TypedQNameConverter.cs | 12 +++++++-----
.../DocumentFormat.OpenXml.Generator.Models.csproj | 2 +-
.../OpenXmlGeneratorDataSource.cs | 10 +++++-----
.../SourceGenerator.targets | 5 +----
test/Directory.Build.targets | 1 -
7 files changed, 21 insertions(+), 23 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 12b0f3705..5de32fc7a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,7 +15,6 @@
-
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
index 16a5febd1..144055fd3 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/QualifiedNameConverter.cs
@@ -2,25 +2,26 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Generator.Models;
-using Newtonsoft.Json;
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace DocumentFormat.OpenXml.Generator.Converters;
internal class QualifiedNameConverter : JsonConverter
{
- public override QName? ReadJson(JsonReader reader, Type objectType, QName? existingValue, bool hasExistingValue, JsonSerializer serializer)
+ public override QName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- if (reader.TokenType != JsonToken.String)
+ if (reader.TokenType != JsonTokenType.String)
{
throw new InvalidOperationException("QName must be encoded as a string");
}
- var str = serializer.Deserialize(reader) ?? string.Empty;
-
+ var str = reader.GetString() ?? string.Empty;
return QName.Parse(str);
}
- public override void WriteJson(JsonWriter writer, QName? value, JsonSerializer serializer)
+ public override void Write(Utf8JsonWriter writer, QName value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
index acecdcdbc..70456f10b 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/Converters/TypedQNameConverter.cs
@@ -2,20 +2,22 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using DocumentFormat.OpenXml.Generator.Models;
-using Newtonsoft.Json;
+using System;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace DocumentFormat.OpenXml.Generator.Converters;
internal class TypedQNameConverter : JsonConverter
{
- public override TypedQName? ReadJson(JsonReader reader, Type objectType, TypedQName? existingValue, bool hasExistingValue, JsonSerializer serializer)
+ public override TypedQName? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
- if (reader.TokenType != JsonToken.String)
+ if (reader.TokenType != JsonTokenType.String)
{
throw new InvalidOperationException("TypedQName must be encoded as a string");
}
- var str = serializer.Deserialize(reader) ?? string.Empty;
+ var str = reader.GetString() ?? string.Empty;
var split = str.Split('/');
if (split.Length != 2)
@@ -26,7 +28,7 @@ internal class TypedQNameConverter : JsonConverter
return new TypedQName(QName.Parse(split[0]), QName.Parse(split[1]));
}
- public override void WriteJson(JsonWriter writer, TypedQName? value, JsonSerializer serializer)
+ public override void Write(Utf8JsonWriter writer, TypedQName value, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj b/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
index d5ebe540a..6a11c3d57 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/DocumentFormat.OpenXml.Generator.Models.csproj
@@ -9,7 +9,7 @@
DocumentFormat.OpenXml.Generator
-
+
diff --git a/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs b/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
index 9ff0ef930..869fa3849 100644
--- a/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
+++ b/gen/DocumentFormat.OpenXml.Generator.Models/OpenXmlGeneratorDataSource.cs
@@ -4,25 +4,25 @@
using DocumentFormat.OpenXml.Generator.Converters;
using DocumentFormat.OpenXml.Generator.Models;
using DocumentFormat.OpenXml.Generator.Schematron;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Converters;
using System.Collections.Immutable;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace DocumentFormat.OpenXml.Generator;
public record OpenXmlGeneratorDataSource
{
- private static readonly JsonSerializerSettings _settings = new()
+ private static readonly JsonSerializerOptions _options = new()
{
Converters =
{
- new StringEnumConverter(),
+ new JsonStringEnumConverter(),
new QualifiedNameConverter(),
new TypedQNameConverter(),
},
};
- public static T? Deserialize(string? content) => content is null ? default : JsonConvert.DeserializeObject(content, _settings);
+ public static T? Deserialize(string? content) => content is null ? default : JsonSerializer.Deserialize(content, _options);
public ImmutableArray KnownNamespaces { get; init; } = ImmutableArray.Create();
diff --git a/gen/DocumentFormat.OpenXml.Generator/SourceGenerator.targets b/gen/DocumentFormat.OpenXml.Generator/SourceGenerator.targets
index 048cd0fe8..6bc09548c 100644
--- a/gen/DocumentFormat.OpenXml.Generator/SourceGenerator.targets
+++ b/gen/DocumentFormat.OpenXml.Generator/SourceGenerator.targets
@@ -7,10 +7,7 @@
-
-
-
-
+
diff --git a/test/Directory.Build.targets b/test/Directory.Build.targets
index c1153f3f8..b83d51d43 100644
--- a/test/Directory.Build.targets
+++ b/test/Directory.Build.targets
@@ -19,7 +19,6 @@
-
From f25b814c308ea421c36edf896104d8f2ab9ae248 Mon Sep 17 00:00:00 2001
From: Taylor Southwick
Date: Thu, 11 Dec 2025 13:57:05 -0800
Subject: [PATCH 16/16] fix up tests to validate json
---
.../TestUtility.cs | 14 +++++++++++
.../ITestOutputHelperExtenstions.cs | 6 ++---
.../PartConstraintRuleTests.cs | 19 +++++++++------
.../ParticleTests.cs | 23 ++++++-------------
.../data/PartConstraintData.json | 20 ++++++++--------
5 files changed, 46 insertions(+), 36 deletions(-)
diff --git a/test/DocumentFormat.OpenXml.Framework.Tests/TestUtility.cs b/test/DocumentFormat.OpenXml.Framework.Tests/TestUtility.cs
index 49c147202..c968a1834 100644
--- a/test/DocumentFormat.OpenXml.Framework.Tests/TestUtility.cs
+++ b/test/DocumentFormat.OpenXml.Framework.Tests/TestUtility.cs
@@ -4,8 +4,10 @@
using System;
using System.IO;
using System.Reflection;
+using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
+using Xunit;
namespace DocumentFormat.OpenXml.Framework.Tests
{
@@ -13,6 +15,7 @@ internal static class TestUtility
{
private static readonly JsonSerializerOptions _options = new()
{
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Converters =
{
new OpenXmlNamespaceConverter(),
@@ -22,6 +25,17 @@ internal static class TestUtility
WriteIndented = true,
};
+ public static void ValidateJsonFileContentsAreEqual(Stream stream1, Stream stream2)
+ {
+ using var reader1 = new StreamReader(stream1);
+ using var reader2 = new StreamReader(stream2);
+
+ var expected = reader1.ReadToEnd().Replace("\r\n", "\n");
+ var actual = reader2.ReadToEnd().Replace("\r\n", "\n");
+
+ Assert.Equal(expected, actual);
+ }
+
#nullable enable
public static T? Deserialize(string name)
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/ITestOutputHelperExtenstions.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/ITestOutputHelperExtenstions.cs
index 920e5df48..1a3c8c77b 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/ITestOutputHelperExtenstions.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/ITestOutputHelperExtenstions.cs
@@ -1,9 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using DocumentFormat.OpenXml.Framework;
using DocumentFormat.OpenXml.Framework.Tests;
-using System;
using System.IO;
using Xunit;
@@ -11,13 +9,15 @@ namespace DocumentFormat.OpenXml.Tests
{
internal static class ITestOutputHelperExtenstions
{
- public static void WriteObjectToTempFile(this ITestOutputHelper output, string name, T obj)
+ public static string WriteObjectToTempFile(this ITestOutputHelper output, string name, T obj)
{
var tmp = Path.GetTempFileName();
output.WriteLine($"Wrote {name} to temp path {tmp}");
File.WriteAllText(tmp, TestUtility.Serialize(obj));
+
+ return tmp;
}
}
}
diff --git a/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs b/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
index 41f8920e8..0e6532c08 100644
--- a/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
+++ b/test/DocumentFormat.OpenXml.Packaging.Tests/PartConstraintRuleTests.cs
@@ -3,7 +3,9 @@
using DocumentFormat.OpenXml.Features;
using DocumentFormat.OpenXml.Framework;
+using DocumentFormat.OpenXml.Framework.Tests;
using DocumentFormat.OpenXml.Packaging;
+using DocumentFormat.OpenXml.Packaging.Tests;
using NSubstitute;
using System;
using System.Collections.Generic;
@@ -86,10 +88,9 @@ public void ValidatePart(Type partType)
}
Assert.NotNull(expectedConstraints.Parts);
-#if DEBUG
+
_output.WriteObjectToTempFile("expected constraints", expectedConstraints.Parts.OrderBy(p => p.RelationshipType));
_output.WriteObjectToTempFile("actual constraints", constraints.Rules.OrderBy(p => p.RelationshipType).Select(p => new PartConstraintRule2(p)));
-#endif
Assert.Equal(
expectedConstraints.Parts.OrderBy(p => p.RelationshipType),
@@ -119,7 +120,13 @@ public void ExportData()
})
.OrderBy(d => d.Name, StringComparer.Ordinal);
- _output.WriteObjectToTempFile("typed parts", result);
+ var output = _output.WriteObjectToTempFile("typed parts", result);
+
+ using (var expectedStream = typeof(ParticleTests).GetTypeInfo().Assembly.GetManifestResourceStream("DocumentFormat.OpenXml.Packaging.Tests.data.PartConstraintData.json"))
+ using (var actualStream = new FileStream(output, FileMode.Open, FileAccess.Read))
+ {
+ TestUtility.ValidateJsonFileContentsAreEqual(expectedStream!, actualStream);
+ }
}
public static IEnumerable