Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
</PropertyGroup>

<PropertyGroup>
<LangVersion>13</LangVersion>
<LangVersion>12</LangVersion>
</PropertyGroup>

<PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<PackageVersion Include="Svg" Version="3.4.7" />
<PackageVersion Include="System.CodeDom" Version="8.0.0" />
<PackageVersion Include="System.IO.Packaging" Version="8.0.1" />
<PackageVersion Include="System.Collections.Immutable" Version="9.0.4" />
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Security.Cryptography.Algorithms" Version="4.3.1" />
<PackageVersion Include="System.Text.Json" Version="9.0.0" />
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.100",
"version": "8.0.100",
"rollForward": "feature"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -157,7 +157,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
94 changes: 34 additions & 60 deletions test/DocumentFormat.OpenXml.Packaging.Tests/ParticleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -207,21 +206,19 @@ public void ValidateExpectedParticles()

private void AssertEqual(Dictionary<Type, VersionCollection<ParticleConstraint>> constraints)
{
var settings = new JsonSerializerSettings
var options = new JsonSerializerOptions
{
Formatting = Formatting.Indented,
Converters = new JsonConverter[]
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNamingPolicy = null, // Use Pascal case like Newtonsoft
Converters =
{
new StringEnumConverter(),
new JsonStringEnumConverter(),
new TypeNameConverter(),
new QNameConverter(),
},
ContractResolver = new OccursDefaultResolver(),
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore,
};

var serializer = JsonSerializer.Create(settings);
var tmp = Path.GetTempFileName();

_output.WriteLine($"Writing output to {tmp}");
Expand All @@ -231,10 +228,17 @@ private void AssertEqual(Dictionary<Type, VersionCollection<ParticleConstraint>>
{
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(" ", " ");

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);
}
}

Expand All @@ -250,44 +254,6 @@ private void AssertEqual(Dictionary<Type, VersionCollection<ParticleConstraint>>
}
}

private class OccursDefaultResolver : DefaultContractResolver
{
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<JsonProperty> 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<ParticleConstraint>);
prop.ShouldSerialize = c => ((CompositeParticle)c).ChildrenParticles.Any();
}
}

return properties.OrderBy(p => p.PropertyName).ToList();
}
}

private class VersionCollection<T> : IEnumerable<KeyValuePair<FileFormatVersions, T>>
{
private readonly List<KeyValuePair<FileFormatVersions, T>> _dic = new List<KeyValuePair<FileFormatVersions, T>>();
Expand Down Expand Up @@ -324,22 +290,30 @@ public void Add(FileFormatVersions key, T value)
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

private class TypeNameConverter : JsonConverter<Type>
private sealed class TypeNameConverter : JsonConverter<Type>
{
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<OpenXmlQualifiedName>
{
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());
}
}
}
}