Skip to content

Commit bdd2635

Browse files
committed
Simplified implementation by not having deeper namespaces. They are now always [your_dll_name_without_extension].Json.[json_filename_csharp_friendly]. Fixes #4
1 parent 7500ac4 commit bdd2635

File tree

5 files changed

+81
-33
lines changed

5 files changed

+81
-33
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<LangVersion>9.0</LangVersion>
44
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
55
<Nullable>enable</Nullable>
6-
<Version>0.8.2</Version>
6+
<Version>0.8.3</Version>
77
</PropertyGroup>
88
</Project>

JsonByExampleGenerator.Generator/JsonGenerator.cs

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,32 @@ public void Execute(GeneratorExecutionContext context)
7272
SourceText.From(onlyOnceGeneratedCode, Encoding.UTF8));
7373

7474
// Resolve all json files that are added to the AdditionalFiles in the compilation
75-
var allJsonFiles = context.AdditionalFiles.Where(f => f.Path.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase));
75+
var allJsonFiles = context
76+
.AdditionalFiles
77+
.Where(f => f.Path.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase))
78+
.ToList();
79+
80+
// If there are multiple files with the same name, report an error
81+
var duplicates = allJsonFiles
82+
.Select(f => Path.GetFileNameWithoutExtension(f.Path))
83+
.GroupBy(f => f)
84+
.Where(f => f.Count() > 1)
85+
.Select(f => f.Key);
86+
if(duplicates.Any())
87+
{
88+
string message = $"Multiple json files found with the same name; JsonByExampleGenerator cannot generate code for this, because it would cause errors. File names: {string.Join(", ", duplicates)}";
89+
context.ReportDiagnostic(Diagnostic.Create(
90+
new DiagnosticDescriptor(
91+
"SI0001",
92+
message,
93+
message,
94+
"JsonByExampleGenerator",
95+
DiagnosticSeverity.Error,
96+
isEnabledByDefault: true),
97+
Location.None));
98+
return;
99+
}
100+
76101
foreach (var jsonFile in allJsonFiles)
77102
{
78103
var jsonFileText = jsonFile.GetText(context.CancellationToken);
@@ -83,8 +108,8 @@ public void Execute(GeneratorExecutionContext context)
83108

84109
var json = JsonDocument.Parse(jsonFileText.ToString());
85110

86-
// Determine the deeper namespace based on the path within the project and the file name
87-
string deeperNamespaceName = GetDeeperNamespaceName(namespaceName, jsonFile);
111+
// Determine the namespace based on the file name
112+
string jsonFileNameBasedNamespace = GetDeeperNamespaceName(namespaceName, jsonFile);
88113

89114
// Read the json and build a list of models that can be used to generate classes
90115
var classModels = new List<ClassModel>();
@@ -130,14 +155,14 @@ public void Execute(GeneratorExecutionContext context)
130155

131156
if (context.Compilation != null)
132157
{
133-
FilterAndChangeBasedOnExistingCode(classModels, deeperNamespaceName, context.Compilation);
158+
FilterAndChangeBasedOnExistingCode(classModels, jsonFileNameBasedNamespace, context.Compilation);
134159
}
135160

136161
// Use Scriban to render the code using the model that was built
137162
string generatedCode = template.Render(new
138163
{
139164
OptionalDependencies = optionalDependencies,
140-
NamespaceName = deeperNamespaceName,
165+
NamespaceName = jsonFileNameBasedNamespace,
141166
ConfigEnabled = configEnabled,
142167
ClassModels = classModels,
143168
RuntimeVersion = RuntimeVersion,
@@ -146,8 +171,8 @@ public void Execute(GeneratorExecutionContext context)
146171
}, member => member.Name);
147172

148173
// Add the generated code to the compilation
149-
context.AddSource(GetSourceFileName(jsonFile.Path),
150-
SourceText.From(generatedCode, Encoding.UTF8));
174+
var hintName = GetSourceFileName(jsonFile.Path);
175+
context.AddSource(hintName, SourceText.From(generatedCode, Encoding.UTF8));
151176
}
152177
}
153178
catch (Exception ex)
@@ -174,23 +199,9 @@ public void Execute(GeneratorExecutionContext context)
174199
/// <returns></returns>
175200
private static string GetDeeperNamespaceName(string namespaceName, AdditionalText jsonFile)
176201
{
177-
string compilationPath = Path.GetFullPath(".");
178-
string relativePath = Path.GetFullPath(jsonFile.Path);
179-
if (relativePath.StartsWith(compilationPath))
180-
{
181-
relativePath = relativePath.Substring(compilationPath.Length);
182-
}
183-
184-
if (relativePath.EndsWith(".json", StringComparison.InvariantCultureIgnoreCase))
185-
{
186-
relativePath = relativePath.Substring(0, relativePath.Length - 5);
187-
}
188-
189-
var deeperNamespaceParts = relativePath
190-
.Split(new[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)
191-
.Select(r => GetValidName(r));
202+
string fileName = Path.GetFileNameWithoutExtension(jsonFile.Path);
192203

193-
return $"{namespaceName}.Json.{string.Join(".", deeperNamespaceParts)}";
204+
return $"{namespaceName}.Json.{GetValidName(fileName)}";
194205
}
195206

196207
/// <summary>
@@ -200,14 +211,13 @@ private static string GetDeeperNamespaceName(string namespaceName, AdditionalTex
200211
/// <returns></returns>
201212
private string GetSourceFileName(string path)
202213
{
203-
var fullPath = Path.GetFullPath(path)
204-
.Replace(Path.GetFullPath("."), string.Empty)
214+
var fileName = Path.GetFileNameWithoutExtension(path)
205215
.Replace("/", "_")
206216
.Replace("\\", "_")
207217
.Replace(".", "_")
208218
.Replace(":", "_");
209219

210-
return $"{fullPath.Trim('_')}.gen.cs";
220+
return $"{fileName.Trim('_')}.gen.cs";
211221
}
212222

213223
/// <summary>

JsonByExampleGenerator.Tests/ConflictingNamesTests.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using Microsoft.CodeAnalysis;
2+
using System;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Text;
@@ -70,5 +71,42 @@ public static string RunTest()
7071

7172
Assert.Equal("propval", RunTest(compilation));
7273
}
74+
75+
[Fact]
76+
public void ShouldNotGenerateDuplicateJsonFileName()
77+
{
78+
string source = @"using System;
79+
80+
namespace Example
81+
{
82+
class Test
83+
{
84+
public static string RunTest()
85+
{
86+
var json = new TestImplementation.Json.Ex.Ex()
87+
{
88+
Prop = ""propval""
89+
};
90+
return $""{json.Prop}"";
91+
}
92+
}
93+
}";
94+
var diagnostics = new List<Diagnostic>();
95+
96+
var compilation = GetGeneratedOutput(
97+
source,
98+
new Dictionary<string, string>()
99+
{
100+
{ "folder1/ex.json", "{ \"prop\" : \"val\" }" },
101+
{ "folder2/ex.json", "{ \"propsy\" : \"val\" }" }
102+
},
103+
diagnostics);
104+
105+
Assert.Collection(
106+
diagnostics,
107+
d => Assert.Equal(
108+
"Multiple json files found with the same name; JsonByExampleGenerator cannot generate code for this, because it would cause errors. File names: ex",
109+
d.GetMessage()));
110+
}
73111
}
74112
}

JsonByExampleGenerator.Tests/SerializationTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,8 @@ public static string RunTest()
9595
streamWriter.Write(@""{jsonAsString.Replace("\"", "\"\"")}"");
9696
streamWriter.Flush();
9797
readStream.Position = 0;
98-
var ser = new DataContractJsonSerializer(typeof(TestImplementation.Json.{prefixPath}{rootTypeName}.{rootTypeName}));
99-
var rootType = (TestImplementation.Json.{prefixPath}{rootTypeName}.{rootTypeName}) ser.ReadObject(readStream);
98+
var ser = new DataContractJsonSerializer(typeof(TestImplementation.Json.{rootTypeName}.{rootTypeName}));
99+
var rootType = (TestImplementation.Json.{rootTypeName}.{rootTypeName}) ser.ReadObject(readStream);
100100
101101
var writeStream = new MemoryStream();
102102
ser.WriteObject(writeStream, rootType);

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ dotnet add package JsonByExampleGenerator
2121
<AdditionalFiles Include="products.json" />
2222
</ItemGroup>
2323
```
24-
3. You can now use the generated classes in your code. Add a using statement for `[your_dll_name_without_extension].Json.[path_to_json_csharp_friendly]`. E.g.:
24+
3. You can now use the generated classes in your code. Add a using statement for `[your_dll_name_without_extension].Json.[json_filename_csharp_friendly]`. E.g.:
2525
```csharp
2626
// For /mock_files/products.json
27-
using MyCompany.MyProject.Json.MockFiles.Products;
27+
using MyCompany.MyProject.Json.Products;
2828
```
29-
Intellisense should help you out when adding a using statement for this. The complicated namespace is needed to ensure separation between different files.
29+
Intellisense should help you out when adding a using statement for this.
3030

3131
# Example usage
3232

0 commit comments

Comments
 (0)