From c40179484181ed184bd8b97b46ccce010e941f40 Mon Sep 17 00:00:00 2001 From: Rodrigo Cesar Date: Fri, 29 Jul 2022 17:09:58 -0300 Subject: [PATCH 1/5] Insert @deprecated comment when type is Obsolete in C# --- converter.js | 25 ++++++++ lib/csharp-models-to-json/EnumCollector.cs | 5 +- lib/csharp-models-to-json/ModelCollector.cs | 9 +++ lib/csharp-models-to-json/Util.cs | 31 ++++++++++ .../ModelCollector_test.cs | 60 +++++++++++++++++++ test-files/TestFile.cs | 1 + 6 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 lib/csharp-models-to-json/Util.cs diff --git a/converter.js b/converter.js index 104a427..fd16f9d 100644 --- a/converter.js +++ b/converter.js @@ -70,6 +70,9 @@ const createConverter = config => { if (!config.omitFilePathComment) { rows.push(`// ${filename}`); } + if (model.Obsolete) { + rows.push(formatObsoleteMessage(model.ObsoleteMessage, '')); + } rows.push(`export interface ${model.ModelName}${baseClasses} {`); const propertySemicolon = config.omitSemicolon ? '' : ';'; @@ -79,6 +82,9 @@ const createConverter = config => { } members.forEach(member => { + if (member.Obsolete) { + rows.push(formatObsoleteMessage(member.ObsoleteMessage, ' ')); + } rows.push(` ${convertProperty(member)}${propertySemicolon}`); }); @@ -95,6 +101,10 @@ const createConverter = config => { const entries = Object.entries(enum_.Values); + if (enum_.Obsolete) { + rows.push(formatObsoleteMessage(enum_.ObsoleteMessage, '')); + } + const getEnumStringValue = (value) => config.camelCaseEnums ? camelcase(value) : value; @@ -131,6 +141,21 @@ const createConverter = config => { return rows; }; + const formatObsoleteMessage = (obsoleteMessage, identation) => { + if (obsoleteMessage) { + obsoleteMessage = ' ' + obsoleteMessage; + } else { + obsoleteMessage = ''; + } + + let deprecationMessage = ''; + deprecationMessage += `${identation}/**\n`; + deprecationMessage += `${identation} * @deprecated${obsoleteMessage}\n`; + deprecationMessage += `${identation} */`; + + return deprecationMessage; + } + const convertProperty = property => { const optional = property.Type.endsWith('?'); const identifier = convertIdentifier(optional ? `${property.Identifier.split(' ')[0]}?` : property.Identifier.split(' ')[0]); diff --git a/lib/csharp-models-to-json/EnumCollector.cs b/lib/csharp-models-to-json/EnumCollector.cs index 412fdea..7ee48f9 100644 --- a/lib/csharp-models-to-json/EnumCollector.cs +++ b/lib/csharp-models-to-json/EnumCollector.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -8,6 +7,8 @@ namespace CSharpModelsToJson public class Enum { public string Identifier { get; set; } + public bool Obsolete { get; set; } + public string ObsoleteMessage { get; set; } public Dictionary Values { get; set; } } @@ -29,6 +30,8 @@ public override void VisitEnumDeclaration(EnumDeclarationSyntax node) this.Enums.Add(new Enum() { Identifier = node.Identifier.ToString(), + Obsolete = Util.IsObsolete(node.AttributeLists), + ObsoleteMessage = Util.GetObsoleteMessage(node.AttributeLists), Values = values }); } diff --git a/lib/csharp-models-to-json/ModelCollector.cs b/lib/csharp-models-to-json/ModelCollector.cs index e9e3f80..0d3fd3b 100644 --- a/lib/csharp-models-to-json/ModelCollector.cs +++ b/lib/csharp-models-to-json/ModelCollector.cs @@ -12,6 +12,9 @@ public class Model public IEnumerable Fields { get; set; } public IEnumerable Properties { get; set; } public IEnumerable BaseClasses { get; set; } + public bool Obsolete { get; set; } + public string ObsoleteMessage { get; set; } + } public class Field @@ -24,6 +27,8 @@ public class Property { public string Identifier { get; set; } public string Type { get; set; } + public bool Obsolete { get; set; } + public string ObsoleteMessage { get; set; } } public class ModelCollector : CSharpSyntaxWalker @@ -81,6 +86,8 @@ private static Model CreateModel(TypeDeclarationSyntax node) .Where(property => !IsIgnored(property.AttributeLists)) .Select(ConvertProperty), BaseClasses = node.BaseList?.Types.Select(s => s.ToString()), + Obsolete = Util.IsObsolete(node.AttributeLists), + ObsoleteMessage = Util.GetObsoleteMessage(node.AttributeLists), }; } @@ -105,6 +112,8 @@ private static bool IsAccessible(SyntaxTokenList modifiers) => modifiers.All(mod { Identifier = property.Identifier.ToString(), Type = property.Type.ToString(), + Obsolete = Util.IsObsolete(property.AttributeLists), + ObsoleteMessage = Util.GetObsoleteMessage(property.AttributeLists) }; } } \ No newline at end of file diff --git a/lib/csharp-models-to-json/Util.cs b/lib/csharp-models-to-json/Util.cs new file mode 100644 index 0000000..e3eee9e --- /dev/null +++ b/lib/csharp-models-to-json/Util.cs @@ -0,0 +1,31 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Linq; + +namespace CSharpModelsToJson +{ + internal static class Util + { + internal static bool IsObsolete(SyntaxList attributeLists) => + attributeLists.Any(attributeList => + attributeList.Attributes.Any(attribute => + attribute.Name.ToString().Equals("Obsolete") || attribute.Name.ToString().Equals("ObsoleteAttribute"))); + + internal static string GetObsoleteMessage(SyntaxList attributeLists) + { + foreach (var attributeList in attributeLists) + { + var obsoleteAttribute = + attributeList.Attributes.FirstOrDefault(attribute => + attribute.Name.ToString().Equals("Obsolete") || attribute.Name.ToString().Equals("ObsoleteAttribute")); + + if (obsoleteAttribute != null) + return obsoleteAttribute.ArgumentList == null + ? null + : obsoleteAttribute.ArgumentList.Arguments.ToString()?.TrimStart('@').Trim('"'); + } + + return null; + } + } +} diff --git a/lib/csharp-models-to-json_test/ModelCollector_test.cs b/lib/csharp-models-to-json_test/ModelCollector_test.cs index c73551d..9dbacfa 100644 --- a/lib/csharp-models-to-json_test/ModelCollector_test.cs +++ b/lib/csharp-models-to-json_test/ModelCollector_test.cs @@ -173,6 +173,66 @@ public void DictionaryInheritance_ReturnsIndexAccessor() Assert.That(modelCollector.Models.First().BaseClasses, Is.EqualTo(new[] { "Dictionary" })); } + [Test] + public void ReturnObsoleteClassInfo() + { + var tree = CSharpSyntaxTree.ParseText(@" + [Obsolete(@""test"")] + public class A + { + [Obsolete(@""test prop"")] + public string A { get; set } + + public string B { get; set } + }" + ); + + var root = (CompilationUnitSyntax)tree.GetRoot(); + + var modelCollector = new ModelCollector(); + modelCollector.VisitClassDeclaration(root.DescendantNodes().OfType().First()); + + var model = modelCollector.Models.First(); + + Assert.That(model, Is.Not.Null); + Assert.That(model.Properties, Is.Not.Null); + + Assert.That(model.Obsolete, Is.True); + Assert.That(model.ObsoleteMessage, Is.EqualTo("test")); + + Assert.That(model.Properties.First(x => x.Identifier.Equals("A")).Obsolete, Is.True); + Assert.That(model.Properties.First(x => x.Identifier.Equals("A")).ObsoleteMessage, Is.EqualTo("test prop")); + + Assert.That(model.Properties.First(x => x.Identifier.Equals("B")).Obsolete, Is.False); + Assert.That(model.Properties.First(x => x.Identifier.Equals("B")).ObsoleteMessage, Is.Null); + } + + [Test] + public void ReturnObsoleteEnumInfo() + { + var tree = CSharpSyntaxTree.ParseText(@" + [Obsolete(@""test"")] + public enum A + { + A = 0, + B = 1, + }" + ); + + var root = (CompilationUnitSyntax)tree.GetRoot(); + + var enumCollector = new EnumCollector(); + enumCollector.VisitEnumDeclaration(root.DescendantNodes().OfType().First()); + + var model = enumCollector.Enums.First(); + + Assert.That(model, Is.Not.Null) ; + Assert.That(model.Values, Is.Not.Null); + + Assert.That(model.Obsolete, Is.True); + Assert.That(model.ObsoleteMessage, Is.EqualTo("test")); + } + [Test] public void EnumBinaryValue() { diff --git a/test-files/TestFile.cs b/test-files/TestFile.cs index 9f66b5e..a15dc5a 100644 --- a/test-files/TestFile.cs +++ b/test-files/TestFile.cs @@ -15,6 +15,7 @@ public class TestClass /// public int IntProperty { get; set; } + [Obsolete("obsolete test prop")] public string StringProperty { get; set; } public DateTime DateTimeProperty { get; set; } From 2e71df5a35e64f121a95ecfbb1a869ce0545de9c Mon Sep 17 00:00:00 2001 From: Rodrigo Cesar Date: Mon, 1 Aug 2022 11:56:18 -0300 Subject: [PATCH 2/5] Add Obsolete message to enum values --- converter.js | 9 +++++--- lib/csharp-models-to-json/EnumCollector.cs | 23 +++++++++++++++---- .../EnumCollector_test.cs | 11 ++++----- .../ModelCollector_test.cs | 12 +++++----- test-files/TestFile.cs | 1 + 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/converter.js b/converter.js index fd16f9d..f9729bd 100644 --- a/converter.js +++ b/converter.js @@ -123,12 +123,15 @@ const createConverter = config => { } else { rows.push(`export enum ${enum_.Identifier} {`); - entries.forEach(([key, value]) => { + entries.forEach(([key, entrie]) => { + if (entrie.Obsolete) { + rows.push(formatObsoleteMessage(entrie.ObsoleteMessage, ' ')); + } if (config.numericEnums) { - if (value == null) { + if (entrie.Value == null) { rows.push(` ${key},`); } else { - rows.push(` ${key} = ${value},`); + rows.push(` ${key} = ${entrie.Value},`); } } else { rows.push(` ${key} = '${getEnumStringValue(key)}',`); diff --git a/lib/csharp-models-to-json/EnumCollector.cs b/lib/csharp-models-to-json/EnumCollector.cs index 7ee48f9..3df38d2 100644 --- a/lib/csharp-models-to-json/EnumCollector.cs +++ b/lib/csharp-models-to-json/EnumCollector.cs @@ -9,23 +9,38 @@ public class Enum public string Identifier { get; set; } public bool Obsolete { get; set; } public string ObsoleteMessage { get; set; } - public Dictionary Values { get; set; } + public Dictionary Values { get; set; } } + public class EnumValue + { + public string Value { get; set; } + public bool Obsolete { get; set; } + public string ObsoleteMessage { get; set; } + } + + public class EnumCollector: CSharpSyntaxWalker { public readonly List Enums = new List(); public override void VisitEnumDeclaration(EnumDeclarationSyntax node) { - var values = new Dictionary(); + var values = new Dictionary(); foreach (var member in node.Members) { - var value = member.EqualsValue != null + var equalsValue = member.EqualsValue != null ? member.EqualsValue.Value.ToString() : null; - values[member.Identifier.ToString()] = value?.Replace("_", ""); + var value = new EnumValue + { + Value = equalsValue?.Replace("_", ""), + Obsolete = Util.IsObsolete(member.AttributeLists), + ObsoleteMessage = Util.GetObsoleteMessage(member.AttributeLists) + }; + + values[member.Identifier.ToString()] = value; } this.Enums.Add(new Enum() { diff --git a/lib/csharp-models-to-json_test/EnumCollector_test.cs b/lib/csharp-models-to-json_test/EnumCollector_test.cs index b7e9883..19b0712 100644 --- a/lib/csharp-models-to-json_test/EnumCollector_test.cs +++ b/lib/csharp-models-to-json_test/EnumCollector_test.cs @@ -32,12 +32,11 @@ public enum SampleEnum Assert.That(model, Is.Not.Null); Assert.That(model.Values, Is.Not.Null); - var values = model.Values.ToArray(); - Assert.That(values[0].Value, Is.Null); - Assert.That(values[1].Value, Is.EqualTo("7")); - Assert.That(values[2].Value, Is.Null); - Assert.That(values[3].Value, Is.EqualTo("4")); - Assert.That(values[4].Value, Is.Null); + Assert.That(model.Values["A"].Value, Is.Null); + Assert.That(model.Values["B"].Value, Is.EqualTo("7")); + Assert.That(model.Values["C"].Value, Is.Null); + Assert.That(model.Values["D"].Value, Is.EqualTo("4")); + Assert.That(model.Values["E"].Value, Is.Null); } } } \ No newline at end of file diff --git a/lib/csharp-models-to-json_test/ModelCollector_test.cs b/lib/csharp-models-to-json_test/ModelCollector_test.cs index 9dbacfa..5c75977 100644 --- a/lib/csharp-models-to-json_test/ModelCollector_test.cs +++ b/lib/csharp-models-to-json_test/ModelCollector_test.cs @@ -257,12 +257,12 @@ public enum A { Assert.That(model, Is.Not.Null); Assert.That(model.Values, Is.Not.Null); - Assert.That(model.Values["A"], Is.EqualTo("1")); - Assert.That(model.Values["B"], Is.EqualTo("1002")); - Assert.That(model.Values["C"], Is.EqualTo("0b011")); - Assert.That(model.Values["D"], Is.EqualTo("0b00000100")); - Assert.That(model.Values["E"], Is.EqualTo("0x005")); - Assert.That(model.Values["F"], Is.EqualTo("0x00001a")); + Assert.That(model.Values["A"].Value, Is.EqualTo("1")); + Assert.That(model.Values["B"].Value, Is.EqualTo("1002")); + Assert.That(model.Values["C"].Value, Is.EqualTo("0b011")); + Assert.That(model.Values["D"].Value, Is.EqualTo("0b00000100")); + Assert.That(model.Values["E"].Value, Is.EqualTo("0x005")); + Assert.That(model.Values["F"].Value, Is.EqualTo("0x00001a")); } } diff --git a/test-files/TestFile.cs b/test-files/TestFile.cs index a15dc5a..846322e 100644 --- a/test-files/TestFile.cs +++ b/test-files/TestFile.cs @@ -30,6 +30,7 @@ public enum TestEnum { D = 0b_0000_0100, // binary: 4 in decimal E = 0x005, // hexadecimal: 5 in decimal F = 0x000_01a, // hexadecimal: 26 in decimal + [Obsolete("obsolete test enum")] G // 27 in decimal } } From 95d8d5264801dbb08233f6fa60c221cf5858acab Mon Sep 17 00:00:00 2001 From: Rodrigo Cesar Date: Tue, 2 Aug 2022 10:37:14 -0300 Subject: [PATCH 3/5] Console output in UTF8 to fix latin caracters --- lib/csharp-models-to-json/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/csharp-models-to-json/Program.cs b/lib/csharp-models-to-json/Program.cs index 023a5c7..b3bddf4 100644 --- a/lib/csharp-models-to-json/Program.cs +++ b/lib/csharp-models-to-json/Program.cs @@ -43,6 +43,7 @@ static void Main(string[] args) sb.AppendLine(json); sb.AppendLine("<<<<<>>>>>"); + System.Console.OutputEncoding = System.Text.Encoding.UTF8; System.Console.WriteLine(sb.ToString()); } From fd9ebbdf012b116f9efaff9673d59b510a089018 Mon Sep 17 00:00:00 2001 From: Rodrigo Cesar Date: Wed, 29 May 2024 10:49:05 -0300 Subject: [PATCH 4/5] Omit null values Does not serialize null objects to reduce the amount of text in the output. An example is the field with the obsolete comment. --- index.js | 2 ++ lib/csharp-models-to-json/Program.cs | 10 ++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index ef5c29a..5cfad02 100755 --- a/index.js +++ b/index.js @@ -60,6 +60,8 @@ dotnetProcess.stderr.on('data', err => { dotnetProcess.stdout.on('end', () => { let json; + //console.log(stdout); + try { // Extract the JSON content between the markers const startMarker = '<<<<<>>>>>'; diff --git a/lib/csharp-models-to-json/Program.cs b/lib/csharp-models-to-json/Program.cs index b3bddf4..dfe9d0b 100644 --- a/lib/csharp-models-to-json/Program.cs +++ b/lib/csharp-models-to-json/Program.cs @@ -1,11 +1,12 @@ using System.Collections.Generic; +using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.Extensions.Configuration; using Ganss.IO; -using System.Text; namespace CSharpModelsToJson { @@ -36,7 +37,12 @@ static void Main(string[] args) files.Add(parseFile(fileName)); } - string json = JsonSerializer.Serialize(files); + JsonSerializerOptions options = new() + { + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }; + + string json = JsonSerializer.Serialize(files, options); var sb = new StringBuilder(); sb.AppendLine("<<<<<>>>>>"); From 86e47eb098e619943e7644d81310579834eb9e03 Mon Sep 17 00:00:00 2001 From: Rodrigo Cesar Date: Thu, 6 Jun 2024 15:07:57 -0300 Subject: [PATCH 5/5] Fix typo --- converter.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/converter.js b/converter.js index f9729bd..9714f88 100644 --- a/converter.js +++ b/converter.js @@ -123,15 +123,15 @@ const createConverter = config => { } else { rows.push(`export enum ${enum_.Identifier} {`); - entries.forEach(([key, entrie]) => { - if (entrie.Obsolete) { - rows.push(formatObsoleteMessage(entrie.ObsoleteMessage, ' ')); + entries.forEach(([key, entry]) => { + if (entry.Obsolete) { + rows.push(formatObsoleteMessage(entry.ObsoleteMessage, ' ')); } if (config.numericEnums) { - if (entrie.Value == null) { + if (entry.Value == null) { rows.push(` ${key},`); } else { - rows.push(` ${key} = ${entrie.Value},`); + rows.push(` ${key} = ${entry.Value},`); } } else { rows.push(` ${key} = '${getEnumStringValue(key)}',`); @@ -144,7 +144,7 @@ const createConverter = config => { return rows; }; - const formatObsoleteMessage = (obsoleteMessage, identation) => { + const formatObsoleteMessage = (obsoleteMessage, indentation) => { if (obsoleteMessage) { obsoleteMessage = ' ' + obsoleteMessage; } else { @@ -152,9 +152,9 @@ const createConverter = config => { } let deprecationMessage = ''; - deprecationMessage += `${identation}/**\n`; - deprecationMessage += `${identation} * @deprecated${obsoleteMessage}\n`; - deprecationMessage += `${identation} */`; + deprecationMessage += `${indentation}/**\n`; + deprecationMessage += `${indentation} * @deprecated${obsoleteMessage}\n`; + deprecationMessage += `${indentation} */`; return deprecationMessage; }