Текущая версия каноникализатора JSON
This commit is contained in:
parent
9ae7538f5f
commit
e143ecd006
14
JsonCanicalizer.Test/JsonCanonicalizer.Test.csproj
Normal file
14
JsonCanicalizer.Test/JsonCanonicalizer.Test.csproj
Normal file
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\JsonCanonicalizer.BL\JsonCanonicalizer.BL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
26
JsonCanicalizer.Test/Program.cs
Normal file
26
JsonCanicalizer.Test/Program.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using JsonCanonicalizer.BL;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace JsonCanicalizer.Test;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
string input = File.ReadAllText(@"d:\Code\testbench\JsonCanonicalizer\JsonCanicalizer.Test\test.json");
|
||||
|
||||
using var doc = JsonDocument.Parse(input);
|
||||
|
||||
var writerOptions = new JsonWriterOptions
|
||||
{
|
||||
Indented = true,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
};
|
||||
|
||||
using var outputStream = new FileStream(@"d:\Code\testbench\JsonCanonicalizer\JsonCanicalizer.Test\result.json", FileMode.Create);
|
||||
using var writer = new Utf8JsonWriter(outputStream, writerOptions);
|
||||
|
||||
Canonicalizer.WriteSorted(writer, doc.RootElement);
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
64
JsonCanicalizer.Test/result.json
Normal file
64
JsonCanicalizer.Test/result.json
Normal file
@ -0,0 +1,64 @@
|
||||
{
|
||||
"serviceTasks": [
|
||||
{
|
||||
"guid": "07e607a8-7673-458f-a8f0-2850f7864327",
|
||||
"id": "Activity_1y13pct",
|
||||
"name": "Внесение изменений/ дополнений/ исключений сведений в реестр уведомлений ТЭД (envelopeData)"
|
||||
},
|
||||
{
|
||||
"guid": "18b61142-b1e7-483a-bb17-3d42db1b8db3",
|
||||
"id": "Activity_0lrt9vk",
|
||||
"name": "Присвоение номера по шаблону"
|
||||
},
|
||||
{
|
||||
"guid": "29203f14-7505-4994-bd84-574524b35c65",
|
||||
"id": "Activity_086flld",
|
||||
"name": "Получение выписки из реестра ТЭД"
|
||||
},
|
||||
{
|
||||
"guid": "2a804098-fba1-4828-a139-c1449c3b2acf",
|
||||
"id": "Activity_1etjhti",
|
||||
"name": "Перевод дела в статус \"Положительное решение\""
|
||||
},
|
||||
{
|
||||
"guid": "406c7066-3eb7-4abd-a7d9-b9f96cc9e236",
|
||||
"id": "Activity_0ptjle4",
|
||||
"name": "Включение в реестр уведомлений ТЭД (envelopeData)"
|
||||
},
|
||||
{
|
||||
"guid": "59b09840-2b0f-47c3-9d07-941e00dc9794",
|
||||
"id": "Activity_1wcy9l4",
|
||||
"name": "Открытые сведения из ЕГРЮЛ (включая сведения, доступ к которым ограничен абзацами 3 и 5 пункта 1 статьи 6 Федерального закона от 08.08.2001 № 129-ФЗ) по запросам органов государственной власти (envelopeData)"
|
||||
},
|
||||
{
|
||||
"guid": "805fe785-5c2b-4ee2-a2e7-c4adbc40ae5d",
|
||||
"id": "Activity_0bwwih4",
|
||||
"name": "Перевод в статус \"Отказ в предоставлении услуги\" (4)"
|
||||
},
|
||||
{
|
||||
"guid": "80dbe463-1df6-4f9f-b76d-888786c1484f",
|
||||
"id": "Activity_0ii8c0k",
|
||||
"name": "Перевод дела в статус \"Отрицательное решение\""
|
||||
},
|
||||
{
|
||||
"guid": "87c1dc1d-b8d6-464c-8eff-48163f5b86ab",
|
||||
"id": "Activity_0hwnhm8",
|
||||
"name": "Отправка на ЕПГУ \"Услуга оказана\" (3) с положительным решением, необходимые файлы получены"
|
||||
},
|
||||
{
|
||||
"guid": "bee4c89c-601f-4211-a615-7ef8a3eeade7",
|
||||
"id": "Activity_14vp8wt",
|
||||
"name": "Получение данных из реестра уведомлений ТЭД (envelopeData)"
|
||||
},
|
||||
{
|
||||
"guid": "e51e3b7c-082c-4555-bbfa-1579c81b9ffb",
|
||||
"id": "Activity_0ux39aa",
|
||||
"name": "Перевод статуса заявления на ЕПГУ \"Заявление принято к рассмотрению\"(6)"
|
||||
},
|
||||
{
|
||||
"guid": "ee174972-ab78-4fc6-979a-37c1384f3c6d",
|
||||
"id": "Activity_1yii4fr",
|
||||
"name": "Сведения из ЕГРИП по запросам органов государственной власти (открытые сведения и сведения о рождении и адресе ИП) (регламентный)"
|
||||
}
|
||||
]
|
||||
}
|
||||
1
JsonCanicalizer.Test/test.json
Normal file
1
JsonCanicalizer.Test/test.json
Normal file
@ -0,0 +1 @@
|
||||
{"serviceTasks":[{"guid":"18b61142-b1e7-483a-bb17-3d42db1b8db3","id":"Activity_0lrt9vk","name":"Присвоение номера по шаблону"},{"guid":"29203f14-7505-4994-bd84-574524b35c65","id":"Activity_086flld","name":"Получение выписки из реестра ТЭД"},{"guid":"80dbe463-1df6-4f9f-b76d-888786c1484f","id":"Activity_0ii8c0k","name":"Перевод дела в статус \"Отрицательное решение\""},{"guid":"805fe785-5c2b-4ee2-a2e7-c4adbc40ae5d","id":"Activity_0bwwih4","name":"Перевод в статус \"Отказ в предоставлении услуги\" (4)"},{"guid":"87c1dc1d-b8d6-464c-8eff-48163f5b86ab","id":"Activity_0hwnhm8","name":"Отправка на ЕПГУ \"Услуга оказана\" (3) с положительным решением, необходимые файлы получены"},{"guid":"2a804098-fba1-4828-a139-c1449c3b2acf","id":"Activity_1etjhti","name":"Перевод дела в статус \"Положительное решение\""},{"guid":"e51e3b7c-082c-4555-bbfa-1579c81b9ffb","id":"Activity_0ux39aa","name":"Перевод статуса заявления на ЕПГУ \"Заявление принято к рассмотрению\"(6)"},{"guid":"ee174972-ab78-4fc6-979a-37c1384f3c6d","id":"Activity_1yii4fr","name":"Сведения из ЕГРИП по запросам органов государственной власти (открытые сведения и сведения о рождении и адресе ИП) (регламентный)"},{"guid":"59b09840-2b0f-47c3-9d07-941e00dc9794","id":"Activity_1wcy9l4","name":"Открытые сведения из ЕГРЮЛ (включая сведения, доступ к которым ограничен абзацами 3 и 5 пункта 1 статьи 6 Федерального закона от 08.08.2001 № 129-ФЗ) по запросам органов государственной власти (envelopeData)"},{"guid":"406c7066-3eb7-4abd-a7d9-b9f96cc9e236","id":"Activity_0ptjle4","name":"Включение в реестр уведомлений ТЭД (envelopeData)"},{"guid":"bee4c89c-601f-4211-a615-7ef8a3eeade7","id":"Activity_14vp8wt","name":"Получение данных из реестра уведомлений ТЭД (envelopeData)"},{"guid":"07e607a8-7673-458f-a8f0-2850f7864327","id":"Activity_1y13pct","name":"Внесение изменений/ дополнений/ исключений сведений в реестр уведомлений ТЭД (envelopeData)"}]}
|
||||
112
JsonCanonicalizer.BL/Canonicalizer.cs
Normal file
112
JsonCanonicalizer.BL/Canonicalizer.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace JsonCanonicalizer.BL;
|
||||
|
||||
public class Canonicalizer
|
||||
{
|
||||
public static void WriteSorted(Utf8JsonWriter writer, JsonElement element)
|
||||
{
|
||||
switch (element.ValueKind)
|
||||
{
|
||||
case JsonValueKind.Object:
|
||||
writer.WriteStartObject();
|
||||
// Сортируем ключи объекта по имени
|
||||
foreach (var property in element.EnumerateObject()
|
||||
.OrderBy(p => p.Name, StringComparer.Ordinal))
|
||||
{
|
||||
writer.WritePropertyName(property.Name);
|
||||
WriteSorted(writer, property.Value);
|
||||
}
|
||||
writer.WriteEndObject();
|
||||
break;
|
||||
|
||||
case JsonValueKind.Array:
|
||||
writer.WriteStartArray();
|
||||
var items = element.EnumerateArray().ToList();
|
||||
|
||||
// Проверяем, нужно ли сортировать массив по guid:
|
||||
// 1. Все элементы — объекты
|
||||
// 2. У каждого объекта есть свойство "guid"
|
||||
bool allAreObjects = items.All(i => i.ValueKind == JsonValueKind.Object);
|
||||
bool allHaveGuid = allAreObjects &&
|
||||
items.All(i => IsOrderable(i));
|
||||
|
||||
IEnumerable<JsonElement> orderedItems = items;
|
||||
if (allHaveGuid)
|
||||
{
|
||||
orderedItems = items.OrderBy(i => GetOrder(i),
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// Рекурсивно обрабатываем каждый элемент (на случай вложенных массивов)
|
||||
foreach (var item in orderedItems)
|
||||
{
|
||||
WriteSorted(writer, item);
|
||||
}
|
||||
writer.WriteEndArray();
|
||||
break;
|
||||
|
||||
case JsonValueKind.String:
|
||||
writer.WriteStringValue(element.GetString());
|
||||
break;
|
||||
|
||||
case JsonValueKind.Number:
|
||||
if (element.TryGetDecimal(out decimal dec))
|
||||
writer.WriteNumberValue(dec);
|
||||
else
|
||||
writer.WriteNumberValue(element.GetDouble());
|
||||
break;
|
||||
|
||||
case JsonValueKind.True:
|
||||
writer.WriteBooleanValue(true);
|
||||
break;
|
||||
|
||||
case JsonValueKind.False:
|
||||
writer.WriteBooleanValue(false);
|
||||
break;
|
||||
|
||||
case JsonValueKind.Null:
|
||||
writer.WriteNullValue();
|
||||
break;
|
||||
|
||||
default:
|
||||
writer.WriteCommentValue(element.GetRawText());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsOrderable(JsonElement e)
|
||||
{
|
||||
return e.TryGetProperty("id", out _) || e.TryGetProperty("guid", out _);
|
||||
}
|
||||
|
||||
static string GetOrder(JsonElement e)
|
||||
{
|
||||
if (e.TryGetProperty("id", out JsonElement id))
|
||||
{
|
||||
return GetGuidValue(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetGuidValue(e.GetProperty("guid"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Универсально извлекает значение guid как строку для сравнения.
|
||||
/// Работает даже если guid почему-то оказался числом или булевым.
|
||||
/// </summary>
|
||||
static string GetGuidValue(JsonElement guidElement)
|
||||
{
|
||||
return guidElement.ValueKind switch
|
||||
{
|
||||
JsonValueKind.String => guidElement.GetString() ?? "",
|
||||
JsonValueKind.Number => guidElement.GetRawText(),
|
||||
JsonValueKind.True => "true",
|
||||
JsonValueKind.False => "false",
|
||||
JsonValueKind.Null => "",
|
||||
_ => guidElement.GetRawText()
|
||||
};
|
||||
}
|
||||
}
|
||||
9
JsonCanonicalizer.BL/JsonCanonicalizer.BL.csproj
Normal file
9
JsonCanonicalizer.BL/JsonCanonicalizer.BL.csproj
Normal file
@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
37
JsonCanonicalizer.sln
Normal file
37
JsonCanonicalizer.sln
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36915.13
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonCanonicalizer", "JsonCanonicalizer\JsonCanonicalizer.csproj", "{35AE0B30-4ACC-4CCF-9FC7-43A7DBF86B54}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonCanonicalizer.BL", "JsonCanonicalizer.BL\JsonCanonicalizer.BL.csproj", "{C4C72D1B-E361-49FB-A76D-6691D27CF2AB}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JsonCanonicalizer.Test", "JsonCanicalizer.Test\JsonCanonicalizer.Test.csproj", "{E43C66DF-FE35-4752-8201-A08E807122DB}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{35AE0B30-4ACC-4CCF-9FC7-43A7DBF86B54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{35AE0B30-4ACC-4CCF-9FC7-43A7DBF86B54}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{35AE0B30-4ACC-4CCF-9FC7-43A7DBF86B54}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{35AE0B30-4ACC-4CCF-9FC7-43A7DBF86B54}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C4C72D1B-E361-49FB-A76D-6691D27CF2AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C4C72D1B-E361-49FB-A76D-6691D27CF2AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C4C72D1B-E361-49FB-A76D-6691D27CF2AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C4C72D1B-E361-49FB-A76D-6691D27CF2AB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E43C66DF-FE35-4752-8201-A08E807122DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E43C66DF-FE35-4752-8201-A08E807122DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E43C66DF-FE35-4752-8201-A08E807122DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E43C66DF-FE35-4752-8201-A08E807122DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {ADE14663-0DC6-43E9-9161-DC0A59CE0881}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
14
JsonCanonicalizer/JsonCanonicalizer.csproj
Normal file
14
JsonCanonicalizer/JsonCanonicalizer.csproj
Normal file
@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\JsonCanonicalizer.BL\JsonCanonicalizer.BL.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
41
JsonCanonicalizer/Program.cs
Normal file
41
JsonCanonicalizer/Program.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace JsonCanonicalizer
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var inputStream = Console.OpenStandardInput();
|
||||
using var doc = JsonDocument.Parse(inputStream);
|
||||
|
||||
var writerOptions = new JsonWriterOptions
|
||||
{
|
||||
Indented = true,
|
||||
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
|
||||
};
|
||||
|
||||
using var outputStream = Console.OpenStandardOutput();
|
||||
using var writer = new Utf8JsonWriter(outputStream, writerOptions);
|
||||
|
||||
BL.Canonicalizer.WriteSorted(writer, doc.RootElement);
|
||||
writer.Flush();
|
||||
|
||||
Console.Out.WriteLine();
|
||||
return 0;
|
||||
}
|
||||
catch (JsonException ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Invalid JSON: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Error: {ex.Message}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
nuget.config
Normal file
7
nuget.config
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
Loading…
Reference in New Issue
Block a user