Skip to content

Commit f0bd18b

Browse files
committed
Implement JSON output formatter for API comparison results
1 parent 223657e commit f0bd18b

File tree

3 files changed

+473
-2
lines changed

3 files changed

+473
-2
lines changed

.kiro/specs/dotnet-api-diff/tasks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,15 @@ Each task should follow this git workflow:
108108
- _Requirements: 3.2, 3.3, 5.1, 5.2, 5.3, 5.4_
109109

110110
- [ ] 6. Implement output formatting and reporting
111-
- [ ] 6.1 Create ReportGenerator and console output formatter
111+
- [x] 6.1 Create ReportGenerator and console output formatter
112112
- Implement ReportGenerator interface with support for multiple formats
113113
- Create ConsoleFormatter with colored output for additions, removals, modifications
114114
- Add summary statistics and breaking change indicators
115115
- Write unit tests for console output formatting
116116
- **Git Workflow**: Create branch `feature/task-6.1-report-generator`, commit, push, and create PR
117117
- _Requirements: 4.4, 8.1, 8.4_
118118

119-
- [ ] 6.2 Implement JSON output formatter
119+
- [x] 6.2 Implement JSON output formatter
120120
- Create JsonFormatter producing structured, valid JSON output
121121
- Include all comparison details suitable for programmatic consumption
122122
- Write unit tests validating JSON structure and content
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright DotNet API Diff Project Contributors - SPDX Identifier: MIT
2+
using DotNetApiDiff.Interfaces;
3+
using DotNetApiDiff.Models;
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
6+
7+
namespace DotNetApiDiff.Reporting;
8+
9+
/// <summary>
10+
/// Formatter for JSON output with complete comparison details
11+
/// </summary>
12+
public class JsonFormatter : IReportFormatter
13+
{
14+
private readonly JsonSerializerOptions _jsonOptions;
15+
16+
/// <summary>
17+
/// Initializes a new instance of the <see cref="JsonFormatter"/> class.
18+
/// </summary>
19+
/// <param name="indented">Whether to format the JSON with indentation</param>
20+
public JsonFormatter(bool indented = true)
21+
{
22+
_jsonOptions = new JsonSerializerOptions
23+
{
24+
WriteIndented = indented,
25+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
26+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
27+
};
28+
}
29+
30+
/// <summary>
31+
/// Formats a comparison result as JSON
32+
/// </summary>
33+
/// <param name="result">The comparison result to format</param>
34+
/// <returns>Formatted JSON output as a string</returns>
35+
public string Format(ComparisonResult result)
36+
{
37+
// Create a JSON-friendly representation of the comparison result
38+
var jsonModel = CreateJsonModel(result);
39+
40+
// Serialize to JSON
41+
return JsonSerializer.Serialize(jsonModel, _jsonOptions);
42+
}
43+
44+
private static JsonComparisonResult CreateJsonModel(ComparisonResult result)
45+
{
46+
// Create a JSON-specific model that includes all necessary information
47+
var jsonResult = new JsonComparisonResult
48+
{
49+
OldAssemblyPath = result.OldAssemblyPath,
50+
NewAssemblyPath = result.NewAssemblyPath,
51+
ComparisonTimestamp = result.ComparisonTimestamp,
52+
HasBreakingChanges = result.HasBreakingChanges,
53+
TotalDifferences = result.TotalDifferences,
54+
Summary = new JsonComparisonSummary
55+
{
56+
AddedCount = result.Summary.AddedCount,
57+
RemovedCount = result.Summary.RemovedCount,
58+
ModifiedCount = result.Summary.ModifiedCount,
59+
BreakingChangesCount = result.Summary.BreakingChangesCount,
60+
TotalChanges = result.Summary.TotalChanges
61+
}
62+
};
63+
64+
// Group differences by change type for better organization
65+
var addedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Added).ToList();
66+
var removedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Removed).ToList();
67+
var modifiedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Modified).ToList();
68+
var excludedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Excluded).ToList();
69+
var movedItems = result.Differences.Where(d => d.ChangeType == ChangeType.Moved).ToList();
70+
71+
// Convert differences to JSON model
72+
jsonResult.Added = addedItems.Select(ConvertToJsonDifference).ToList();
73+
jsonResult.Removed = removedItems.Select(ConvertToJsonDifference).ToList();
74+
jsonResult.Modified = modifiedItems.Select(ConvertToJsonDifference).ToList();
75+
jsonResult.Excluded = excludedItems.Select(ConvertToJsonDifference).ToList();
76+
jsonResult.Moved = movedItems.Select(ConvertToJsonDifference).ToList();
77+
78+
// Add breaking changes separately for easy access
79+
jsonResult.BreakingChanges = result.Differences
80+
.Where(d => d.IsBreakingChange)
81+
.Select(ConvertToJsonDifference)
82+
.ToList();
83+
84+
return jsonResult;
85+
}
86+
87+
private static JsonApiDifference ConvertToJsonDifference(ApiDifference difference)
88+
{
89+
return new JsonApiDifference
90+
{
91+
ChangeType = difference.ChangeType.ToString(),
92+
ElementType = difference.ElementType.ToString(),
93+
ElementName = difference.ElementName,
94+
Description = difference.Description,
95+
IsBreakingChange = difference.IsBreakingChange,
96+
Severity = difference.Severity.ToString(),
97+
OldSignature = difference.OldSignature,
98+
NewSignature = difference.NewSignature
99+
};
100+
}
101+
102+
#region JSON Models
103+
104+
/// <summary>
105+
/// JSON-specific representation of a comparison result
106+
/// </summary>
107+
private class JsonComparisonResult
108+
{
109+
public string OldAssemblyPath { get; set; } = string.Empty;
110+
public string NewAssemblyPath { get; set; } = string.Empty;
111+
public DateTime ComparisonTimestamp { get; set; }
112+
public bool HasBreakingChanges { get; set; }
113+
public int TotalDifferences { get; set; }
114+
public JsonComparisonSummary Summary { get; set; } = new();
115+
public List<JsonApiDifference> Added { get; set; } = new();
116+
public List<JsonApiDifference> Removed { get; set; } = new();
117+
public List<JsonApiDifference> Modified { get; set; } = new();
118+
public List<JsonApiDifference> Excluded { get; set; } = new();
119+
public List<JsonApiDifference> Moved { get; set; } = new();
120+
public List<JsonApiDifference> BreakingChanges { get; set; } = new();
121+
}
122+
123+
/// <summary>
124+
/// JSON-specific representation of comparison summary
125+
/// </summary>
126+
private class JsonComparisonSummary
127+
{
128+
public int AddedCount { get; set; }
129+
public int RemovedCount { get; set; }
130+
public int ModifiedCount { get; set; }
131+
public int BreakingChangesCount { get; set; }
132+
public int TotalChanges { get; set; }
133+
}
134+
135+
/// <summary>
136+
/// JSON-specific representation of an API difference
137+
/// </summary>
138+
private class JsonApiDifference
139+
{
140+
public string ChangeType { get; set; } = string.Empty;
141+
public string ElementType { get; set; } = string.Empty;
142+
public string ElementName { get; set; } = string.Empty;
143+
public string Description { get; set; } = string.Empty;
144+
public bool IsBreakingChange { get; set; }
145+
public string Severity { get; set; } = string.Empty;
146+
public string? OldSignature { get; set; }
147+
public string? NewSignature { get; set; }
148+
}
149+
150+
#endregion
151+
}

0 commit comments

Comments
 (0)