Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f8290d4
Initial plan for issue
Copilot May 21, 2025
298bf17
Initial plan for removing Newtonsoft.Json in favor of System.Text.Json
Copilot May 21, 2025
9cb2c6e
Replace Newtonsoft.Json with System.Text.Json in tests and generator …
Copilot May 21, 2025
282fab4
Remove Newtonsoft.Json package references
Copilot May 21, 2025
b1dd0f4
Update ParticleTests.cs JSON formatting
Copilot May 21, 2025
3e4a652
Merge branch 'main' into copilot/fix-1931
twsouthwick Dec 9, 2025
3a5700f
Revert generator models to use Newtonsoft.Json and fix StyleCop warnings
Copilot Dec 9, 2025
ff723c1
Downgrade System.Collections.Immutable to 8.0.0 for source generator …
Copilot Dec 9, 2025
029f016
Revert LangVersion to 13 and SDK version to 9.0.100
Copilot Dec 9, 2025
e5dcca3
Use native System.Text.Json indentation with Utf8JsonWriter
Copilot Dec 9, 2025
319e255
Revert System.Collections.Immutable to 9.0.4
Copilot Dec 9, 2025
d3415c5
Remove conversion-related comment from ParticleTests.cs
Copilot Dec 9, 2025
41d62db
Add ParticleConstraintConverter to replicate OccursDefaultResolver be…
Copilot Dec 9, 2025
117a12a
Fix ParticleConstraintConverter to handle AnyParticle and ElementPart…
Copilot Dec 10, 2025
298724b
Update ParticleConstraintConverter to use reflection for all properties
Copilot Dec 10, 2025
4d5c446
Remove all Newtonsoft.Json references from codebase
Copilot Dec 10, 2025
de13984
Merge branch 'main' into copilot/fix-1931
twsouthwick Dec 10, 2025
f25b814
fix up tests to validate json
twsouthwick Dec 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
<PackageVersion Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.1.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="NuGet.Common" Version="6.13.2" />
<PackageVersion Include="NuGet.Packaging" Version="6.13.2" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<QName>
{
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<string>(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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<TypedQName>
{
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<string>(reader) ?? string.Empty;
var str = reader.GetString() ?? string.Empty;
var split = str.Split('/');

if (split.Length != 2)
Expand All @@ -26,7 +28,7 @@ internal class TypedQNameConverter : JsonConverter<TypedQName>
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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<RootNamespace>DocumentFormat.OpenXml.Generator</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" PrivateAssets="all" />
<PackageReference Include="System.Text.Json" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Collections.Immutable" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(string? content) => content is null ? default : JsonConvert.DeserializeObject<T>(content, _settings);
public static T? Deserialize<T>(string? content) => content is null ? default : JsonSerializer.Deserialize<T>(content, _options);

public ImmutableArray<NamespaceInfo> KnownNamespaces { get; init; } = ImmutableArray.Create<NamespaceInfo>();

Expand Down
5 changes: 1 addition & 4 deletions gen/DocumentFormat.OpenXml.Generator/SourceGenerator.targets
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
<ItemGroup>
<ProjectReference Include="@(OoxGenerator)" OutputItemType="Analyzer" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" GlobalPropertiesToRemove="TargetFramework" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" PrivateAssets="all" GeneratePathProperty="true" />
<Analyzer Include="$(PkgNewtonsoft_Json)\lib\netstandard2.0\*.dll" Visible="false" />
</ItemGroup>

<ItemGroup>
<AdditionalFiles Include="@(OpenXmlDataNamespace)" OpenXml="Namespace" LinkBase="data" />
<AdditionalFiles Include="@(OpenXmlDataSchematron)" OpenXml="Schematron" LinkBase="data" />
Expand Down
1 change: 0 additions & 1 deletion test/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="System.Text.Json" />
<PackageReference Include="System.ValueTuple" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
Expand Down
14 changes: 14 additions & 0 deletions test/DocumentFormat.OpenXml.Framework.Tests/TestUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@
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
{
internal static class TestUtility
{
private static readonly JsonSerializerOptions _options = new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Converters =
{
new OpenXmlNamespaceConverter(),
Expand All @@ -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<T>(string name)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
// 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;

namespace DocumentFormat.OpenXml.Tests
{
internal static class ITestOutputHelperExtenstions
{
public static void WriteObjectToTempFile<T>(this ITestOutputHelper output, string name, T obj)
public static string WriteObjectToTempFile<T>(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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@

using DocumentFormat.OpenXml.Features;
using DocumentFormat.OpenXml.Framework;
using DocumentFormat.OpenXml.Framework.Tests;
using DocumentFormat.OpenXml.Packaging;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using DocumentFormat.OpenXml.Packaging.Tests;
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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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<object[]> GetOpenXmlParts() => GetParts().Select(p => new[] { p });
Expand All @@ -134,9 +141,7 @@ private static IEnumerable<Type> GetParts() => typeof(SpreadsheetDocument)

private static OpenXmlPart InitializePart(Type type)
{
#nullable disable
var part = (OpenXmlPart)Activator.CreateInstance(type, true);
#nullable enable
var part = (OpenXmlPart)Activator.CreateInstance(type, true)!;

var appType = Substitute.For<IApplicationTypeFeature>();
appType.Type.Returns(ApplicationType.None);
Expand All @@ -148,7 +153,7 @@ private static OpenXmlPart InitializePart(Type type)

private static ConstraintData GetConstraintData(OpenXmlPart part) => _cachedConstraintData.Value[part.GetType().FullName!];

private static Lazy<Dictionary<string, ConstraintData>> _cachedConstraintData = new Lazy<Dictionary<string, ConstraintData>>(() =>
private static readonly Lazy<Dictionary<string, ConstraintData>> _cachedConstraintData = new(() =>
{
var names = typeof(PartConstraintRuleTests).GetTypeInfo().Assembly.GetManifestResourceNames();

Expand All @@ -157,7 +162,15 @@ private static OpenXmlPart InitializePart(Type type)
using (var reader = new StreamReader(stream!))
{
#nullable disable
return JsonConvert.DeserializeObject<ConstraintData[]>(reader.ReadToEnd(), new StringEnumConverter())
var options = new JsonSerializerOptions
{
Converters =
{
new JsonStringEnumConverter(),
},
};

return JsonSerializer.Deserialize<ConstraintData[]>(reader.ReadToEnd(), options)
.ToDictionary(t => t.Name, StringComparer.Ordinal);
#nullable enable
}
Expand Down
Loading
Loading