Compare commits
No commits in common. "585447193dd950c8b691c80c8acdf43231ae637a" and "82a59eebd2f9195857951d134b998e9fc44d7a2d" have entirely different histories.
585447193d
...
82a59eebd2
@ -1,5 +1,4 @@
|
|||||||
using DevDisciples.Json.Parser.Syntax;
|
using DevDisciples.Json.Parser.Syntax;
|
||||||
using DevDisciples.Parsing.Extensions;
|
|
||||||
|
|
||||||
namespace DevDisciples.Json.Parser.Tests;
|
namespace DevDisciples.Json.Parser.Tests;
|
||||||
|
|
||||||
@ -111,7 +110,7 @@ public class JsonParserTests
|
|||||||
var node = JsonParser.Parse(nameof(Can_parse_an_object_with_one_entry), source);
|
var node = JsonParser.Parse(nameof(Can_parse_an_object_with_one_entry), source);
|
||||||
|
|
||||||
Assert.That(node is JsonObjectSyntax { Properties.Length: 1 });
|
Assert.That(node is JsonObjectSyntax { Properties.Length: 1 });
|
||||||
var @object = node.As<JsonObjectSyntax>();
|
var @object = (JsonObjectSyntax)node;
|
||||||
Assert.That(@object.Properties.Any(property => property.Key.Lexeme == "first_name"));
|
Assert.That(@object.Properties.Any(property => property.Key.Lexeme == "first_name"));
|
||||||
Assert.That(@object.Properties.First(property => property.Key.Lexeme == "first_name").Value is JsonStringSyntax { Value: "John" });
|
Assert.That(@object.Properties.First(property => property.Key.Lexeme == "first_name").Value is JsonStringSyntax { Value: "John" });
|
||||||
}
|
}
|
||||||
@ -125,7 +124,7 @@ public class JsonParserTests
|
|||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(node is JsonObjectSyntax { Properties.Length: 2 });
|
Assert.That(node is JsonObjectSyntax { Properties.Length: 2 });
|
||||||
var @object = node.As<JsonObjectSyntax>();
|
var @object = (JsonObjectSyntax)node;
|
||||||
Assert.That(@object.Properties.Any(property => property.Key.Lexeme == "first_name"));
|
Assert.That(@object.Properties.Any(property => property.Key.Lexeme == "first_name"));
|
||||||
Assert.That(@object.Properties.First(property => property.Key.Lexeme == "first_name").Value is JsonStringSyntax { Value: "John" });
|
Assert.That(@object.Properties.First(property => property.Key.Lexeme == "first_name").Value is JsonStringSyntax { Value: "John" });
|
||||||
Assert.That(@object.Properties.Any(property => property.Key.Lexeme == "last_name"));
|
Assert.That(@object.Properties.Any(property => property.Key.Lexeme == "last_name"));
|
||||||
|
@ -33,7 +33,7 @@ public static partial class JsonParser
|
|||||||
if (ctx.Match(JsonToken.True) || ctx.Match(JsonToken.False))
|
if (ctx.Match(JsonToken.True) || ctx.Match(JsonToken.False))
|
||||||
return BoolExpression(ctx);
|
return BoolExpression(ctx);
|
||||||
|
|
||||||
throw Report.SyntaxException(ctx.Current, $"Expected a JSON expression, got '{ctx.Current.Lexeme}'");
|
throw Report.Error(ctx.Current, $"Expected a JSON expression, got '{ctx.Current.Lexeme}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IJsonSyntax ArrayExpression(ParserContext<JsonToken> ctx)
|
private static IJsonSyntax ArrayExpression(ParserContext<JsonToken> ctx)
|
||||||
|
@ -17,8 +17,8 @@ public class TransformController : ControllerBase
|
|||||||
return Ok(new { Result = result });
|
return Ok(new { Result = result });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("beautify")]
|
[HttpPost("prettify")]
|
||||||
public IActionResult Beautify([FromBody] BeautifyRequest request)
|
public IActionResult Prettify([FromBody] PrettifyRequest request)
|
||||||
{
|
{
|
||||||
var context = new JsonFormatter.Context { Beautify = true, IndentSize = request.IndentSize };
|
var context = new JsonFormatter.Context { Beautify = true, IndentSize = request.IndentSize };
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ public class GlobalExceptionHandler : IExceptionHandler
|
|||||||
{
|
{
|
||||||
switch (exception)
|
switch (exception)
|
||||||
{
|
{
|
||||||
case SyntaxException:
|
case ParsingException:
|
||||||
_logger.LogError(exception, "An exception occurred: {Message}", exception.Message);
|
_logger.LogError(exception, "An exception occurred: {Message}", exception.Message);
|
||||||
|
|
||||||
var problem = new ProblemDetails
|
var problem = new ProblemDetails
|
||||||
|
@ -1,21 +1,9 @@
|
|||||||
using System.Threading.RateLimiting;
|
|
||||||
using DevDisciples.Json.Tools.API;
|
using DevDisciples.Json.Tools.API;
|
||||||
using Microsoft.AspNetCore.RateLimiting;
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddRateLimiter(o => o
|
|
||||||
.AddSlidingWindowLimiter(policyName: "sliding", options =>
|
|
||||||
{
|
|
||||||
options.PermitLimit = 10; // Maximum 10 requests per 1-second window
|
|
||||||
options.Window = TimeSpan.FromSeconds(1);
|
|
||||||
options.SegmentsPerWindow = 10;
|
|
||||||
options.QueueLimit = 2;
|
|
||||||
options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
|
|
||||||
}));
|
|
||||||
|
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
||||||
@ -34,8 +22,6 @@ builder.Services.AddSwaggerGen();
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseRateLimiter();
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
@ -49,6 +35,6 @@ app.UseHttpsRedirection();
|
|||||||
|
|
||||||
app.UseCors("default");
|
app.UseCors("default");
|
||||||
|
|
||||||
app.MapControllers().RequireRateLimiting("sliding");
|
app.MapControllers();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
@ -12,7 +12,7 @@
|
|||||||
"http": {
|
"http": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": true,
|
||||||
"launchUrl": "swagger",
|
"launchUrl": "swagger",
|
||||||
"applicationUrl": "http://localhost:5000",
|
"applicationUrl": "http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
@ -22,7 +22,7 @@
|
|||||||
"https": {
|
"https": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": true,
|
||||||
"launchUrl": "swagger",
|
"launchUrl": "swagger",
|
||||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace DevDisciples.Json.Tools.API.Requests;
|
namespace DevDisciples.Json.Tools.API.Requests;
|
||||||
|
|
||||||
public class BeautifyRequest : TransformRequest
|
public class PrettifyRequest : TransformRequest
|
||||||
{
|
{
|
||||||
public int IndentSize { get; init; } = JsonFormatter.Context.DefaultIndentSize;
|
public int IndentSize { get; init; } = JsonFormatter.Context.DefaultIndentSize;
|
||||||
}
|
}
|
@ -1,13 +1,13 @@
|
|||||||
import { Routes } from '@angular/router';
|
import { Routes } from '@angular/router';
|
||||||
import {HomeComponent} from "./home/home.component";
|
import {HomeComponent} from "./home/home.component";
|
||||||
import {BeautifyComponent} from "./beautify/beautify.component";
|
import {PrettifyComponent} from "./prettify/prettify.component";
|
||||||
import {UglifyComponent} from "./uglify/uglify.component";
|
import {UglifyComponent} from "./uglify/uglify.component";
|
||||||
import {Json2CsharpComponent} from "./json2csharp/json2-csharp.component";
|
import {Json2CsharpComponent} from "./json2csharp/json2-csharp.component";
|
||||||
import {JsonPathComponent} from "./json-path/json-path.component";
|
import {JsonPathComponent} from "./json-path/json-path.component";
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{ path: 'home', component: HomeComponent },
|
{ path: 'home', component: HomeComponent },
|
||||||
{ path: 'beautify', component: BeautifyComponent },
|
{ path: 'prettify', component: PrettifyComponent },
|
||||||
{ path: 'uglify', component: UglifyComponent },
|
{ path: 'uglify', component: UglifyComponent },
|
||||||
{ path: 'json2csharp', component: Json2CsharpComponent },
|
{ path: 'json2csharp', component: Json2CsharpComponent },
|
||||||
{ path: 'jsonpath', component: JsonPathComponent },
|
{ path: 'jsonpath', component: JsonPathComponent },
|
||||||
|
@ -11,8 +11,8 @@ export class JsonTransformService {
|
|||||||
constructor(private http: HttpClient) {
|
constructor(private http: HttpClient) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public beautify(source: string): Observable<HttpResponse<any>> {
|
public prettify(source: string): Observable<HttpResponse<any>> {
|
||||||
return this.http.post(`${this.url}/api/transform/beautify`, {Source: source}, {observe: "response"});
|
return this.http.post(`${this.url}/api/transform/prettify`, {Source: source}, {observe: "response"});
|
||||||
}
|
}
|
||||||
|
|
||||||
public uglify(source: string): Observable<HttpResponse<any>> {
|
public uglify(source: string): Observable<HttpResponse<any>> {
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<mat-toolbar>Menu</mat-toolbar>
|
<mat-toolbar>Menu</mat-toolbar>
|
||||||
<mat-nav-list>
|
<mat-nav-list>
|
||||||
<mat-list-item routerLink="/home">Home</mat-list-item>
|
<mat-list-item routerLink="/home">Home</mat-list-item>
|
||||||
<mat-list-item routerLink="/beautify">Beautify</mat-list-item>
|
<mat-list-item routerLink="/prettify">Prettify</mat-list-item>
|
||||||
<mat-list-item routerLink="/uglify">Uglify</mat-list-item>
|
<mat-list-item routerLink="/uglify">Uglify</mat-list-item>
|
||||||
<mat-list-item routerLink="/json2csharp">JSON to C#</mat-list-item>
|
<mat-list-item routerLink="/json2csharp">JSON to C#</mat-list-item>
|
||||||
<mat-list-item routerLink="/jsonpath">JSON Path</mat-list-item>
|
<mat-list-item routerLink="/jsonpath">JSON Path</mat-list-item>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<h1>JSON Beautify</h1>
|
<h1>JSON Prettify</h1>
|
||||||
|
|
||||||
<app-input-output [input]="$input.value"
|
<app-input-output [input]="$input.value"
|
||||||
[inputOptions]="inputOptions"
|
[inputOptions]="inputOptions"
|
@ -1,18 +1,18 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { BeautifyComponent } from './beautify.component';
|
import { PrettifyComponent } from './prettify.component';
|
||||||
|
|
||||||
describe('BeautifyComponent', () => {
|
describe('PrettifyComponent', () => {
|
||||||
let component: BeautifyComponent;
|
let component: PrettifyComponent;
|
||||||
let fixture: ComponentFixture<BeautifyComponent>;
|
let fixture: ComponentFixture<PrettifyComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [BeautifyComponent]
|
imports: [PrettifyComponent]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(BeautifyComponent);
|
fixture = TestBed.createComponent(PrettifyComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
@ -12,17 +12,17 @@ import {
|
|||||||
import {BehaviorSubject, debounceTime} from "rxjs";
|
import {BehaviorSubject, debounceTime} from "rxjs";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-beautify',
|
selector: 'app-prettify',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [
|
||||||
EditorComponent,
|
EditorComponent,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
InputOutputComponent
|
InputOutputComponent
|
||||||
],
|
],
|
||||||
templateUrl: './beautify.component.html',
|
templateUrl: './prettify.component.html',
|
||||||
styleUrl: './beautify.component.scss'
|
styleUrl: './prettify.component.scss'
|
||||||
})
|
})
|
||||||
export class BeautifyComponent implements OnInit {
|
export class PrettifyComponent implements OnInit {
|
||||||
// input: string = ;
|
// input: string = ;
|
||||||
$input: BehaviorSubject<string> = new BehaviorSubject<string>(GenerateDefaultJsonObjectString());
|
$input: BehaviorSubject<string> = new BehaviorSubject<string>(GenerateDefaultJsonObjectString());
|
||||||
inputOptions = MonacoJsonConfig;
|
inputOptions = MonacoJsonConfig;
|
||||||
@ -43,7 +43,7 @@ export class BeautifyComponent implements OnInit {
|
|||||||
|
|
||||||
update(input: string): void {
|
update(input: string): void {
|
||||||
this.service
|
this.service
|
||||||
.beautify(input)
|
.prettify(input)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: response => {
|
next: response => {
|
||||||
console.log(response);
|
console.log(response);
|
@ -2,7 +2,7 @@ namespace DevDisciples.Json.Tools.CLI;
|
|||||||
|
|
||||||
public class CommandOptions
|
public class CommandOptions
|
||||||
{
|
{
|
||||||
public InputSource Input { get; set; }
|
public InputOptions Input { get; set; }
|
||||||
public FileInfo? InputFile { get; set; }
|
public FileInfo? InputFile { get; set; }
|
||||||
public string? InputArgument { get; set; }
|
public string? InputArgument { get; set; }
|
||||||
public OutputOptions Output { get; set; }
|
public OutputOptions Output { get; set; }
|
||||||
|
@ -4,12 +4,13 @@ namespace DevDisciples.Json.Tools.CLI.Extensions;
|
|||||||
|
|
||||||
public static class CommandExtensions
|
public static class CommandExtensions
|
||||||
{
|
{
|
||||||
public static void AddInputOutputCommandOptions(this Command command)
|
public static void AddIOCommandOptions(this Command command)
|
||||||
{
|
{
|
||||||
command.AddOption(SharedCommandOptions.InputOption);
|
command.AddOption(SharedCommandOptions.InputOption);
|
||||||
command.AddOption(SharedCommandOptions.InputFileOption);
|
command.AddOption(SharedCommandOptions.InputFileOption);
|
||||||
command.AddArgument(SharedCommandOptions.InputArgument);
|
|
||||||
command.AddOption(SharedCommandOptions.OutputOption);
|
command.AddOption(SharedCommandOptions.OutputOption);
|
||||||
command.AddOption(SharedCommandOptions.OutputFileOption);
|
command.AddOption(SharedCommandOptions.OutputFileOption);
|
||||||
|
|
||||||
|
command.AddArgument(SharedCommandOptions.InputArgument);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,28 +2,28 @@
|
|||||||
|
|
||||||
namespace DevDisciples.Json.Tools.CLI;
|
namespace DevDisciples.Json.Tools.CLI;
|
||||||
|
|
||||||
public static class InputOutputHandler
|
public static class IOHandler
|
||||||
{
|
{
|
||||||
public static async Task<string> HandleInput(InputSource input, string? inputArgument, FileInfo? inputFile)
|
public static async Task<string> HandleInput(InputOptions input, string? inputArgument, FileInfo? inputFile)
|
||||||
{
|
{
|
||||||
string json;
|
string json;
|
||||||
|
|
||||||
switch (input)
|
switch (input)
|
||||||
{
|
{
|
||||||
case InputSource.s:
|
case InputOptions.s:
|
||||||
case InputSource.std:
|
case InputOptions.std:
|
||||||
case InputSource.stdin:
|
case InputOptions.stdin:
|
||||||
json = inputArgument ?? string.Empty;
|
json = inputArgument ?? string.Empty;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InputSource.c:
|
case InputOptions.c:
|
||||||
case InputSource.clip:
|
case InputOptions.clip:
|
||||||
case InputSource.clipboard:
|
case InputOptions.clipboard:
|
||||||
json = await ClipboardService.GetTextAsync() ?? string.Empty;
|
json = await ClipboardService.GetTextAsync() ?? string.Empty;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case InputSource.f:
|
case InputOptions.f:
|
||||||
case InputSource.file:
|
case InputOptions.file:
|
||||||
if (inputFile is null)
|
if (inputFile is null)
|
||||||
throw new ArgumentException("Input file was not specified.");
|
throw new ArgumentException("Input file was not specified.");
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
namespace DevDisciples.Json.Tools.CLI;
|
namespace DevDisciples.Json.Tools.CLI;
|
||||||
|
|
||||||
public enum InputSource
|
public enum InputOptions
|
||||||
{
|
{
|
||||||
// Standard input
|
// Standard input
|
||||||
stdin,
|
stdin,
|
@ -23,7 +23,7 @@ public partial class Json2CSharpCommand : Command
|
|||||||
AddAlias("2cs");
|
AddAlias("2cs");
|
||||||
AddAlias("2c#");
|
AddAlias("2c#");
|
||||||
|
|
||||||
this.AddInputOutputCommandOptions();
|
this.AddIOCommandOptions();
|
||||||
AddOption(RootClassNameOption);
|
AddOption(RootClassNameOption);
|
||||||
AddOption(NamespaceOption);
|
AddOption(NamespaceOption);
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ public partial class Json2CSharpCommand : Command
|
|||||||
|
|
||||||
private static async Task ExecuteAsync(CommandOptions options)
|
private static async Task ExecuteAsync(CommandOptions options)
|
||||||
{
|
{
|
||||||
var json = await InputOutputHandler.HandleInput(options.Input, options.InputArgument, options.InputFile);
|
var json = await IOHandler.HandleInput(options.Input, options.InputArgument, options.InputFile);
|
||||||
|
|
||||||
var output = Json2CSharpTranslator.Translate(json, new()
|
var output = Json2CSharpTranslator.Translate(json, new()
|
||||||
{
|
{
|
||||||
@ -40,6 +40,6 @@ public partial class Json2CSharpCommand : Command
|
|||||||
Namespace = options.Namespace ?? Json2CSharpTranslator.Context.DefaultNamespace
|
Namespace = options.Namespace ?? Json2CSharpTranslator.Context.DefaultNamespace
|
||||||
});
|
});
|
||||||
|
|
||||||
await InputOutputHandler.HandleOutput(options.Output, options.OutputFile, output);
|
await IOHandler.HandleOutput(options.Output, options.OutputFile, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +0,0 @@
|
|||||||
namespace DevDisciples.Json.Tools.CLI;
|
|
||||||
|
|
||||||
public partial class JsonPathCommand
|
|
||||||
{
|
|
||||||
public class CommandOptions : CLI.CommandOptions
|
|
||||||
{
|
|
||||||
public string PathExpression { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
using System.CommandLine.Binding;
|
|
||||||
using DevDisciples.Json.Tools.CLI.Extensions;
|
|
||||||
|
|
||||||
namespace DevDisciples.Json.Tools.CLI;
|
|
||||||
|
|
||||||
public partial class JsonPathCommand
|
|
||||||
{
|
|
||||||
public class CommandOptionsBinder : BinderBase<CommandOptions>
|
|
||||||
{
|
|
||||||
protected override CommandOptions GetBoundValue(BindingContext context) =>
|
|
||||||
new CommandOptions
|
|
||||||
{
|
|
||||||
PathExpression = context.ParseResult.GetValueForOption(PathExpressionOption)
|
|
||||||
}.WithCommonBindings(context);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using System.CommandLine;
|
|
||||||
using DevDisciples.Json.Tools.CLI.Extensions;
|
|
||||||
|
|
||||||
namespace DevDisciples.Json.Tools.CLI;
|
|
||||||
|
|
||||||
public partial class JsonPathCommand : Command
|
|
||||||
{
|
|
||||||
private static readonly Option<string> PathExpressionOption =
|
|
||||||
new(
|
|
||||||
aliases: ["--expr", "-e"],
|
|
||||||
description: "The path expression", getDefaultValue: () => "$"
|
|
||||||
) { IsRequired = true };
|
|
||||||
|
|
||||||
public JsonPathCommand() : base("path", "Evaluate a JSON path expression")
|
|
||||||
{
|
|
||||||
AddAlias("p");
|
|
||||||
this.AddInputOutputCommandOptions();
|
|
||||||
this.AddOption(PathExpressionOption);
|
|
||||||
this.SetHandler(ExecuteAsync, new CommandOptionsBinder());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task ExecuteAsync(CommandOptions options)
|
|
||||||
{
|
|
||||||
var json = await InputOutputHandler.HandleInput(options.Input, options.InputArgument, options.InputFile);
|
|
||||||
|
|
||||||
var output = JsonPath.Interpreter.Evaluate(json, options.PathExpression);
|
|
||||||
|
|
||||||
var beautifiedOutput = JsonFormatter.Format(output, new() { Beautify = true });
|
|
||||||
|
|
||||||
await InputOutputHandler.HandleOutput(options.Output, options.OutputFile, beautifiedOutput);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
namespace DevDisciples.Json.Tools.CLI;
|
namespace DevDisciples.Json.Tools.CLI;
|
||||||
|
|
||||||
public partial class JsonBeautifyCommand
|
public partial class JsonPrettifyCommand
|
||||||
{
|
{
|
||||||
public class CommandOptions : CLI.CommandOptions
|
public class CommandOptions : CLI.CommandOptions
|
||||||
{
|
{
|
@ -3,7 +3,7 @@ using DevDisciples.Json.Tools.CLI.Extensions;
|
|||||||
|
|
||||||
namespace DevDisciples.Json.Tools.CLI;
|
namespace DevDisciples.Json.Tools.CLI;
|
||||||
|
|
||||||
public partial class JsonBeautifyCommand
|
public partial class JsonPrettifyCommand
|
||||||
{
|
{
|
||||||
public class CommandOptionsBinder : BinderBase<CommandOptions>
|
public class CommandOptionsBinder : BinderBase<CommandOptions>
|
||||||
{
|
{
|
@ -3,22 +3,23 @@ using DevDisciples.Json.Tools.CLI.Extensions;
|
|||||||
|
|
||||||
namespace DevDisciples.Json.Tools.CLI;
|
namespace DevDisciples.Json.Tools.CLI;
|
||||||
|
|
||||||
public partial class JsonBeautifyCommand : Command
|
public partial class JsonPrettifyCommand : Command
|
||||||
{
|
{
|
||||||
private static readonly Option<int> IndentSizeOption =
|
private static readonly Option<int> IndentSizeOption =
|
||||||
new(aliases: ["--indent", "--is"], description: "The indent size", getDefaultValue: () => 2);
|
new(aliases: ["--indent", "--is"], description: "The indent size", getDefaultValue: () => 2);
|
||||||
|
|
||||||
public JsonBeautifyCommand() : base("beautify", "Beautify JSON")
|
public JsonPrettifyCommand() : base("prettify", "Prettify JSON")
|
||||||
{
|
{
|
||||||
AddAlias("b");
|
AddAlias("p");
|
||||||
this.AddInputOutputCommandOptions();
|
this.AddIOCommandOptions();
|
||||||
AddOption(IndentSizeOption);
|
AddOption(IndentSizeOption);
|
||||||
|
|
||||||
this.SetHandler(ExecuteAsync, new CommandOptionsBinder());
|
this.SetHandler(ExecuteAsync, new CommandOptionsBinder());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task ExecuteAsync(CommandOptions options)
|
private static async Task ExecuteAsync(CommandOptions options)
|
||||||
{
|
{
|
||||||
var json = await InputOutputHandler.HandleInput(options.Input, options.InputArgument, options.InputFile);
|
var json = await IOHandler.HandleInput(options.Input, options.InputArgument, options.InputFile);
|
||||||
|
|
||||||
var output = JsonFormatter.Format(json, new()
|
var output = JsonFormatter.Format(json, new()
|
||||||
{
|
{
|
||||||
@ -26,6 +27,6 @@ public partial class JsonBeautifyCommand : Command
|
|||||||
IndentSize = options.IndentSize
|
IndentSize = options.IndentSize
|
||||||
});
|
});
|
||||||
|
|
||||||
await InputOutputHandler.HandleOutput(options.Output, options.OutputFile, output);
|
await IOHandler.HandleOutput(options.Output, options.OutputFile, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,16 +8,16 @@ public class JsonUglifyCommand : Command
|
|||||||
public JsonUglifyCommand() : base("uglify", "Uglify JSON")
|
public JsonUglifyCommand() : base("uglify", "Uglify JSON")
|
||||||
{
|
{
|
||||||
AddAlias("u");
|
AddAlias("u");
|
||||||
this.AddInputOutputCommandOptions();
|
this.AddIOCommandOptions();
|
||||||
this.SetHandler(ExecuteAsync, new CommandOptionsBinder());
|
this.SetHandler(ExecuteAsync, new CommandOptionsBinder());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task ExecuteAsync(CommandOptions options)
|
private static async Task ExecuteAsync(CommandOptions options)
|
||||||
{
|
{
|
||||||
var json = await InputOutputHandler.HandleInput(options.Input, options.InputArgument, options.InputFile);
|
var json = await IOHandler.HandleInput(options.Input, options.InputArgument, options.InputFile);
|
||||||
|
|
||||||
var output = JsonFormatter.Format(json, new() { Beautify = false });
|
var output = JsonFormatter.Format(json, new() { Beautify = false });
|
||||||
|
|
||||||
await InputOutputHandler.HandleOutput(options.Output, options.OutputFile, output);
|
await IOHandler.HandleOutput(options.Output, options.OutputFile, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,8 +7,7 @@ public class RootCommand : System.CommandLine.RootCommand
|
|||||||
public RootCommand() : base("A JSON transform CLI tool by DevDisciples.")
|
public RootCommand() : base("A JSON transform CLI tool by DevDisciples.")
|
||||||
{
|
{
|
||||||
AddCommand(new JsonUglifyCommand());
|
AddCommand(new JsonUglifyCommand());
|
||||||
AddCommand(new JsonBeautifyCommand());
|
AddCommand(new JsonPrettifyCommand());
|
||||||
AddCommand(new Json2CSharpCommand());
|
AddCommand(new Json2CSharpCommand());
|
||||||
AddCommand(new JsonPathCommand());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,8 +4,8 @@ namespace DevDisciples.Json.Tools.CLI;
|
|||||||
|
|
||||||
public static class SharedCommandOptions
|
public static class SharedCommandOptions
|
||||||
{
|
{
|
||||||
public static readonly Option<InputSource> InputOption =
|
public static readonly Option<InputOptions> InputOption =
|
||||||
new(aliases: ["--input", "-i"], getDefaultValue: () => InputSource.stdin) { IsRequired = false };
|
new(aliases: ["--input", "-i"], getDefaultValue: () => InputOptions.stdin) { IsRequired = false };
|
||||||
|
|
||||||
public static readonly Argument<string?> InputArgument =
|
public static readonly Argument<string?> InputArgument =
|
||||||
new(name: "input", description: "The input argument.", getDefaultValue: () => default);
|
new(name: "input", description: "The input argument.", getDefaultValue: () => default);
|
||||||
|
0
DevDisciples.Json.Tools.CLI/scripts/publish_as_tool.sh
Executable file → Normal file
0
DevDisciples.Json.Tools.CLI/scripts/publish_as_tool.sh
Executable file → Normal file
@ -1,11 +0,0 @@
|
|||||||
using TranslationScope = DevDisciples.Json.Tools.Json2CSharpTranslator.Context.TranslationScope;
|
|
||||||
|
|
||||||
namespace DevDisciples.Json.Tools.Extensions;
|
|
||||||
|
|
||||||
public static class TranslationScopeExtensions
|
|
||||||
{
|
|
||||||
public static bool IsChildOf(this Stack<TranslationScope> stack, TranslationScope type)
|
|
||||||
{
|
|
||||||
return stack.Count > 1 && stack.ElementAt(1) == type;
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,21 +11,17 @@ public static partial class Json2CSharpTranslator
|
|||||||
|
|
||||||
public void Translate(Context context)
|
public void Translate(Context context)
|
||||||
{
|
{
|
||||||
|
|
||||||
context.Builder.Append($"public class {Name.Pascalize()}\n");
|
context.Builder.Append($"public class {Name.Pascalize()}\n");
|
||||||
context.Builder.Append("{\n");
|
context.Builder.Append("{\n");
|
||||||
|
|
||||||
if (Properties.Count != 0)
|
var last = Properties.Last();
|
||||||
|
foreach (var property in Properties)
|
||||||
{
|
{
|
||||||
var last = Properties.Last();
|
property.Translate(context);
|
||||||
foreach (var property in Properties)
|
context.Builder.Append(property.Equals(last) ? string.Empty : "\n");
|
||||||
{
|
|
||||||
property.Translate(context);
|
|
||||||
context.Builder.Append(property.Equals(last) ? string.Empty : "\n");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Builder.Append("}\n\n");
|
context.Builder.Append("}\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,4 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using Humanizer;
|
|
||||||
|
|
||||||
namespace DevDisciples.Json.Tools;
|
namespace DevDisciples.Json.Tools;
|
||||||
|
|
||||||
@ -7,32 +6,14 @@ public static partial class Json2CSharpTranslator
|
|||||||
{
|
{
|
||||||
public class Context
|
public class Context
|
||||||
{
|
{
|
||||||
public enum TranslationScope
|
|
||||||
{
|
|
||||||
Array,
|
|
||||||
Object,
|
|
||||||
}
|
|
||||||
|
|
||||||
public const string DefaultRootClassName = "Root";
|
public const string DefaultRootClassName = "Root";
|
||||||
public const string DefaultNamespace = "My.Namespace";
|
public const string DefaultNamespace = "My.Namespace";
|
||||||
|
|
||||||
public string RootClassName { get; init; } = DefaultRootClassName;
|
public string RootClassName { get; init; } = DefaultRootClassName;
|
||||||
public string Namespace { get; init; } = DefaultNamespace;
|
public string Namespace { get; init; } = DefaultNamespace;
|
||||||
public Stack<string> Name { get; init; } = new();
|
public string CurrentName { get; set; } = string.Empty;
|
||||||
public Stack<ClassTranslation> Class { get; init; } = new();
|
|
||||||
public Stack<TranslationScope> Scope { get; init; } = new();
|
|
||||||
public List<ClassTranslation> Classes { get; } = new();
|
public List<ClassTranslation> Classes { get; } = new();
|
||||||
public readonly StringBuilder Builder = new();
|
public readonly StringBuilder Builder = new();
|
||||||
|
|
||||||
public string CurrentClassName =>
|
|
||||||
string.Join(
|
|
||||||
string.Empty,
|
|
||||||
Class
|
|
||||||
.SkipLast(1)
|
|
||||||
.Select(cls => cls.Name.Pascalize().Singularize())
|
|
||||||
.Prepend(Name.Peek().Pascalize().Singularize())
|
|
||||||
.Reverse()
|
|
||||||
.ToArray()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
using DevDisciples.Json.Parser;
|
using DevDisciples.Json.Parser;
|
||||||
using DevDisciples.Json.Parser.Syntax;
|
using DevDisciples.Json.Parser.Syntax;
|
||||||
using DevDisciples.Json.Tools.Extensions;
|
|
||||||
using DevDisciples.Parsing;
|
using DevDisciples.Parsing;
|
||||||
using DevDisciples.Parsing.Extensions;
|
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
|
|
||||||
namespace DevDisciples.Json.Tools;
|
namespace DevDisciples.Json.Tools;
|
||||||
@ -21,13 +19,13 @@ public static partial class Json2CSharpTranslator
|
|||||||
Visitors.Register<JsonNullSyntax>(Null);
|
Visitors.Register<JsonNullSyntax>(Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Translate(string input, Context? context = null)
|
public static string Translate(string source, Context? context = null)
|
||||||
{
|
{
|
||||||
if (JsonParser.Parse("<input>", input) is not JsonObjectSyntax root)
|
if (JsonParser.Parse("<source>", source) is not JsonObjectSyntax root)
|
||||||
throw new SyntaxException("Expected a JSON object.");
|
throw new ParsingException("Expected a JSON object.");
|
||||||
|
|
||||||
context ??= new();
|
context ??= new();
|
||||||
context.Name.Push(context.RootClassName);
|
context.CurrentName = context.RootClassName;
|
||||||
|
|
||||||
var visitor = Visitors[typeof(JsonObjectSyntax)];
|
var visitor = Visitors[typeof(JsonObjectSyntax)];
|
||||||
visitor(root, context);
|
visitor(root, context);
|
||||||
@ -36,93 +34,55 @@ public static partial class Json2CSharpTranslator
|
|||||||
context.Builder.Append("//using System.Collections.Generic;\n");
|
context.Builder.Append("//using System.Collections.Generic;\n");
|
||||||
context.Builder.Append('\n');
|
context.Builder.Append('\n');
|
||||||
context.Builder.Append($"namespace {context.Namespace};\n\n");
|
context.Builder.Append($"namespace {context.Namespace};\n\n");
|
||||||
context.Classes.Reverse();
|
|
||||||
context.Classes.ForEach(@class => @class.Translate(context));
|
context.Classes.ForEach(@class => @class.Translate(context));
|
||||||
|
|
||||||
context.Name.Pop();
|
|
||||||
|
|
||||||
return context.Builder.ToString();
|
return context.Builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ITranslation Translate(IJsonSyntax visitee, Context context)
|
|
||||||
{
|
|
||||||
return Visitors[visitee.GetType()](visitee, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ITranslation Object(IJsonSyntax visitee, Context context)
|
private static ITranslation Object(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var @object = visitee.As<JsonObjectSyntax>();
|
var @object = (JsonObjectSyntax)visitee;
|
||||||
var @class = new ClassTranslation { Name = context.CurrentClassName, Properties = new() };
|
var @class = new ClassTranslation
|
||||||
|
{
|
||||||
|
Name = context.CurrentName,
|
||||||
|
Properties = new()
|
||||||
|
};
|
||||||
|
|
||||||
context.Class.Push(@class);
|
context.Classes.Add(@class);
|
||||||
|
|
||||||
context.Scope.Push(Context.TranslationScope.Object);
|
|
||||||
|
|
||||||
foreach (var prop in @object.Properties)
|
foreach (var prop in @object.Properties)
|
||||||
{
|
{
|
||||||
context.Name.Push(prop.Key.Lexeme);
|
context.CurrentName = prop.Key.Lexeme;
|
||||||
|
var visitor = Visitors[prop.Value.GetType()];
|
||||||
var translation = Translate(prop.Value, context);
|
var translation = visitor(prop.Value, context);
|
||||||
|
|
||||||
switch (translation)
|
switch (translation)
|
||||||
{
|
{
|
||||||
case ClassTranslation:
|
case ClassTranslation:
|
||||||
// TODO: Handle class exists
|
@class.Properties.Add(new PropertyTranslation
|
||||||
context.Class.Peek().Properties.Add(new PropertyTranslation
|
|
||||||
{
|
{
|
||||||
Type = translation.Name,
|
Type = translation.Name,
|
||||||
Name = prop.Key.Lexeme,
|
Name = translation.Name,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PropertyTranslation property:
|
case PropertyTranslation property:
|
||||||
// TODO: Handle property exists
|
@class.Properties.Add(property);
|
||||||
context.Class.Peek().Properties.Add(property);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Name.Pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!context.Scope.IsChildOf(Context.TranslationScope.Array))
|
return @class;
|
||||||
{
|
|
||||||
context.Classes.Add(@class);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Scope.Pop();
|
|
||||||
|
|
||||||
return context.Class.Pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ITranslation Array(IJsonSyntax visitee, Context context)
|
private static ITranslation Array(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var array = visitee.As<JsonArraySyntax>();
|
var array = (JsonArraySyntax)visitee;
|
||||||
|
|
||||||
var type = "object";
|
var type = "object";
|
||||||
|
|
||||||
context.Scope.Push(Context.TranslationScope.Array);
|
if (array.Elements.All(e => e is JsonObjectSyntax))
|
||||||
|
|
||||||
if (array.Elements.Length == 0)
|
|
||||||
{
|
{
|
||||||
type = "object";
|
context.Classes.Add(SquashObjects(context.CurrentName, array.Elements, context));
|
||||||
}
|
type = context.Classes.Last().Name;
|
||||||
else if (array.Elements.All(e => e is JsonObjectSyntax))
|
|
||||||
{
|
|
||||||
type = context.CurrentClassName;
|
|
||||||
|
|
||||||
var composite = array.Elements
|
|
||||||
.Select(el => Translate(el, context))
|
|
||||||
.Cast<ClassTranslation>();
|
|
||||||
|
|
||||||
var @class = new ClassTranslation
|
|
||||||
{
|
|
||||||
Name = type,
|
|
||||||
Properties = composite
|
|
||||||
.SelectMany(cls => cls.Properties)
|
|
||||||
.ToList(),
|
|
||||||
};
|
|
||||||
|
|
||||||
context.Classes.Add(@class);
|
|
||||||
}
|
}
|
||||||
else if (array.Elements.All(e => e is JsonStringSyntax es && DateTime.TryParse(es.Value, out _)))
|
else if (array.Elements.All(e => e is JsonStringSyntax es && DateTime.TryParse(es.Value, out _)))
|
||||||
{
|
{
|
||||||
@ -137,24 +97,22 @@ public static partial class Json2CSharpTranslator
|
|||||||
type = array.Elements.Any(e => ((JsonNumberSyntax)e).Token.Lexeme.Contains('.')) ? "double" : "int";
|
type = array.Elements.Any(e => ((JsonNumberSyntax)e).Token.Lexeme.Contains('.')) ? "double" : "int";
|
||||||
}
|
}
|
||||||
|
|
||||||
context.Scope.Pop();
|
|
||||||
|
|
||||||
return new PropertyTranslation
|
return new PropertyTranslation
|
||||||
{
|
{
|
||||||
Type = $"List<{type}>",
|
Type = $"List<{type}>",
|
||||||
Name = context.Name.Peek(),
|
Name = context.CurrentName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ITranslation String(IJsonSyntax visitee, Context context)
|
private static ITranslation String(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var @string = visitee.As<JsonStringSyntax>();
|
var @string = (JsonStringSyntax)visitee;
|
||||||
var type = DateTime.TryParse(@string.Value, out _) ? "DateTime" : "string";
|
var type = DateTime.TryParse(@string.Value, out _) ? "DateTime" : "string";
|
||||||
|
|
||||||
return new PropertyTranslation
|
return new PropertyTranslation
|
||||||
{
|
{
|
||||||
Type = type,
|
Type = type,
|
||||||
Name = context.Name.Peek(),
|
Name = context.CurrentName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,8 +120,8 @@ public static partial class Json2CSharpTranslator
|
|||||||
{
|
{
|
||||||
return new PropertyTranslation
|
return new PropertyTranslation
|
||||||
{
|
{
|
||||||
Type = visitee.As<JsonNumberSyntax>().Token.Lexeme.Contains('.') ? "double" : "int",
|
Type = ((JsonNumberSyntax)visitee).Token.Lexeme.Contains('.') ? "double" : "int",
|
||||||
Name = context.Name.Peek(),
|
Name = context.CurrentName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,7 +130,7 @@ public static partial class Json2CSharpTranslator
|
|||||||
return new PropertyTranslation
|
return new PropertyTranslation
|
||||||
{
|
{
|
||||||
Type = "bool",
|
Type = "bool",
|
||||||
Name = context.Name.Peek(),
|
Name = context.CurrentName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +139,27 @@ public static partial class Json2CSharpTranslator
|
|||||||
return new PropertyTranslation
|
return new PropertyTranslation
|
||||||
{
|
{
|
||||||
Type = "object",
|
Type = "object",
|
||||||
Name = context.Name.Peek(),
|
Name = context.CurrentName,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ClassTranslation SquashObjects(string className, IEnumerable<IJsonSyntax> objects, Context context)
|
||||||
|
{
|
||||||
|
var classes = objects
|
||||||
|
.Select(@object => Object(@object, context))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var squashed = new ClassTranslation
|
||||||
|
{
|
||||||
|
Name = className.Singularize(),
|
||||||
|
Properties = new()
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var @class in classes)
|
||||||
|
foreach (var prop in ((ClassTranslation)@class).Properties)
|
||||||
|
if (squashed.Properties.All(p => p.Name != prop.Name))
|
||||||
|
squashed.Properties.Add(prop);
|
||||||
|
|
||||||
|
return squashed;
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,7 +1,6 @@
|
|||||||
using DevDisciples.Json.Parser;
|
using DevDisciples.Json.Parser;
|
||||||
using DevDisciples.Json.Parser.Syntax;
|
using DevDisciples.Json.Parser.Syntax;
|
||||||
using DevDisciples.Parsing;
|
using DevDisciples.Parsing;
|
||||||
using DevDisciples.Parsing.Extensions;
|
|
||||||
|
|
||||||
namespace DevDisciples.Json.Tools;
|
namespace DevDisciples.Json.Tools;
|
||||||
|
|
||||||
@ -19,9 +18,9 @@ public static partial class JsonFormatter
|
|||||||
Visitors.Register<JsonNullSyntax>(PrintNull);
|
Visitors.Register<JsonNullSyntax>(PrintNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string Format(string input, Context? context)
|
public static string Format(string source, Context? context)
|
||||||
{
|
{
|
||||||
var node = JsonParser.Parse("<input>", input);
|
var node = JsonParser.Parse("<source>", source);
|
||||||
return Format(node, context);
|
return Format(node, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +33,7 @@ public static partial class JsonFormatter
|
|||||||
|
|
||||||
private static void PrintArray(IJsonSyntax visitee, Context context)
|
private static void PrintArray(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var array = visitee.As<JsonArraySyntax>();
|
var array = (JsonArraySyntax)visitee;
|
||||||
|
|
||||||
context.Builder.Append($"[{context.NewLine}");
|
context.Builder.Append($"[{context.NewLine}");
|
||||||
context.IncrementDepth();
|
context.IncrementDepth();
|
||||||
@ -53,7 +52,7 @@ public static partial class JsonFormatter
|
|||||||
|
|
||||||
private static void PrintObject(IJsonSyntax visitee, Context context)
|
private static void PrintObject(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var @object = visitee.As<JsonObjectSyntax>();
|
var @object = (JsonObjectSyntax)visitee;
|
||||||
|
|
||||||
context.Builder.Append($"{{{context.NewLine}");
|
context.Builder.Append($"{{{context.NewLine}");
|
||||||
context.IncrementDepth();
|
context.IncrementDepth();
|
||||||
@ -73,19 +72,19 @@ public static partial class JsonFormatter
|
|||||||
|
|
||||||
private static void PrintString(IJsonSyntax visitee, Context context)
|
private static void PrintString(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var @string = visitee.As<JsonStringSyntax>();
|
var @string = (JsonStringSyntax)visitee;
|
||||||
context.Builder.Append($"\"{@string.Token.Lexeme}\"");
|
context.Builder.Append($"\"{@string.Token.Lexeme}\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PrintNumber(IJsonSyntax visitee, Context context)
|
private static void PrintNumber(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var number = visitee.As<JsonNumberSyntax>();
|
var number = (JsonNumberSyntax)visitee;
|
||||||
context.Builder.Append($"{number.Value}");
|
context.Builder.Append($"{number.Value}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PrintBool(IJsonSyntax visitee, Context context)
|
private static void PrintBool(IJsonSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var @bool = visitee.As<JsonBoolSyntax>();
|
var @bool = (JsonBoolSyntax)visitee;
|
||||||
context.Builder.Append($"{@bool.Value.ToString().ToLower()}");
|
context.Builder.Append($"{@bool.Value.ToString().ToLower()}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,21 +13,21 @@ public static partial class JsonPath
|
|||||||
|
|
||||||
static Interpreter()
|
static Interpreter()
|
||||||
{
|
{
|
||||||
Visitors.Register<WildCardSyntax>(WildCard);
|
Visitors.Register<WildCardSyntax>(WildCardExpression);
|
||||||
Visitors.Register<PropertyAccessorSyntax>(PropertyAccessor);
|
Visitors.Register<PropertyAccessorSyntax>(PropertyAccessorExpression);
|
||||||
Visitors.Register<PropertySyntax>(Property);
|
Visitors.Register<PropertySyntax>(PropertyExpression);
|
||||||
Visitors.Register<ArrayIndexSyntax>(ArrayIndex);
|
Visitors.Register<ArrayIndexSyntax>(ArrayIndexExpression);
|
||||||
Visitors.Register<ArrayIndexListSyntax>(ArrayIndexList);
|
Visitors.Register<ArrayIndexListSyntax>(ArrayIndexListExpression);
|
||||||
Visitors.Register<ObjectIndexSyntax>(ObjectIndex);
|
Visitors.Register<ObjectIndexSyntax>(ObjectIndexExpression);
|
||||||
Visitors.Register<ObjectIndexListSyntax>(ObjectIndexList);
|
Visitors.Register<ObjectIndexListSyntax>(ObjectIndexListExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IJsonSyntax Evaluate(string input, string path)
|
public static IJsonSyntax Evaluate(string source, string path)
|
||||||
{
|
{
|
||||||
var root = JsonParser.Parse("<input>", input);
|
var root = JsonParser.Parse("<json>", source);
|
||||||
|
|
||||||
if (root is not JsonArraySyntax && root is not JsonObjectSyntax)
|
if (root is not JsonArraySyntax && root is not JsonObjectSyntax)
|
||||||
throw Report.SyntaxException(root.Token, "Expected a JSON array or object.");
|
throw Report.Error(root.Token, "Expected a JSON array or object.");
|
||||||
|
|
||||||
var expressions = Parser.Parse(path);
|
var expressions = Parser.Parse(path);
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ public static partial class JsonPath
|
|||||||
|
|
||||||
private static void Evaluate(IJsonPathSyntax node, Context context) => Visitors[node.GetType()](node, context);
|
private static void Evaluate(IJsonPathSyntax node, Context context) => Visitors[node.GetType()](node, context);
|
||||||
|
|
||||||
private static void WildCard(IJsonPathSyntax visitee, Context context)
|
private static void WildCardExpression(IJsonPathSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
switch (context.Target)
|
switch (context.Target)
|
||||||
{
|
{
|
||||||
@ -57,17 +57,17 @@ public static partial class JsonPath
|
|||||||
|
|
||||||
default:
|
default:
|
||||||
var syntax = visitee.As<WildCardSyntax>();
|
var syntax = visitee.As<WildCardSyntax>();
|
||||||
throw Report.SyntaxException(syntax.Token, "Invalid target for '*'.");
|
throw Report.Error(syntax.Token, "Invalid target for '*'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PropertyAccessor(IJsonPathSyntax visitee, Context context)
|
private static void PropertyAccessorExpression(IJsonPathSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var accessor = visitee.As<PropertyAccessorSyntax>();
|
var accessor = visitee.As<PropertyAccessorSyntax>();
|
||||||
Evaluate(accessor.Getter, context);
|
Evaluate(accessor.Getter, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Property(IJsonPathSyntax visitee, Context context)
|
private static void PropertyExpression(IJsonPathSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var property = visitee.As<PropertySyntax>();
|
var property = visitee.As<PropertySyntax>();
|
||||||
|
|
||||||
@ -90,10 +90,10 @@ public static partial class JsonPath
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ArrayIndex(IJsonPathSyntax visitee, Context context)
|
private static void ArrayIndexExpression(IJsonPathSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
if (context.Target is not JsonArraySyntax array)
|
if (context.Target is not JsonArraySyntax array)
|
||||||
throw Report.SyntaxException(context.Target.Token, "Integer indexes are only allowed on arrays.");
|
throw Report.Error(context.Target.Token, "Integer indexes are only allowed on arrays.");
|
||||||
|
|
||||||
var index = visitee.As<ArrayIndexSyntax>();
|
var index = visitee.As<ArrayIndexSyntax>();
|
||||||
|
|
||||||
@ -102,15 +102,15 @@ public static partial class JsonPath
|
|||||||
if (value >= 0 && value < array.Elements.Length)
|
if (value >= 0 && value < array.Elements.Length)
|
||||||
context.Target = array.Elements[value];
|
context.Target = array.Elements[value];
|
||||||
|
|
||||||
else throw Report.SyntaxException(index.Token, "Index out of range.");
|
else throw Report.Error(index.Token, "Index out of range.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ArrayIndexList(IJsonPathSyntax visitee, Context context)
|
private static void ArrayIndexListExpression(IJsonPathSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
var indices = visitee.As<ArrayIndexListSyntax>();
|
var indices = visitee.As<ArrayIndexListSyntax>();
|
||||||
|
|
||||||
if (context.Target is not JsonArraySyntax array)
|
if (context.Target is not JsonArraySyntax array)
|
||||||
throw Report.SyntaxException(indices.Token, "Integer indices are only allowed on arrays.");
|
throw Report.Error(indices.Token, "Integer indices are only allowed on arrays.");
|
||||||
|
|
||||||
var list = new List<IJsonSyntax>();
|
var list = new List<IJsonSyntax>();
|
||||||
|
|
||||||
@ -125,10 +125,10 @@ public static partial class JsonPath
|
|||||||
context.Target = new JsonArraySyntax(array.Token, list.ToArray());
|
context.Target = new JsonArraySyntax(array.Token, list.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ObjectIndex(IJsonPathSyntax visitee, Context context)
|
private static void ObjectIndexExpression(IJsonPathSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
if (context.Target is not JsonObjectSyntax @object)
|
if (context.Target is not JsonObjectSyntax @object)
|
||||||
throw Report.SyntaxException(context.Target.Token, "String indices are only allowed on objects.");
|
throw Report.Error(context.Target.Token, "String indices are only allowed on objects.");
|
||||||
|
|
||||||
var index = visitee.As<ObjectIndexSyntax>();
|
var index = visitee.As<ObjectIndexSyntax>();
|
||||||
|
|
||||||
@ -142,10 +142,10 @@ public static partial class JsonPath
|
|||||||
context.Target = new JsonNullSyntax(@object.Token);
|
context.Target = new JsonNullSyntax(@object.Token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ObjectIndexList(IJsonPathSyntax visitee, Context context)
|
private static void ObjectIndexListExpression(IJsonPathSyntax visitee, Context context)
|
||||||
{
|
{
|
||||||
if (context.Target is not JsonObjectSyntax @object)
|
if (context.Target is not JsonObjectSyntax @object)
|
||||||
throw Report.SyntaxException(context.Target.Token, "Index strings are only allowed on objects.");
|
throw Report.Error(context.Target.Token, "Index strings are only allowed on objects.");
|
||||||
|
|
||||||
var indices = visitee.As<ObjectIndexListSyntax>();
|
var indices = visitee.As<ObjectIndexListSyntax>();
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ public static partial class JsonPath
|
|||||||
{
|
{
|
||||||
public static List<IJsonPathSyntax> Parse(string path)
|
public static List<IJsonPathSyntax> Parse(string path)
|
||||||
{
|
{
|
||||||
var pathTokens = Lexer.Default.Lex("<path_expression>", path);
|
var pathTokens = Lexer.Default.Lex("<path>", path);
|
||||||
var context = new Context(pathTokens.ToArray());
|
var context = new Context(pathTokens.ToArray());
|
||||||
|
|
||||||
context.Match(JsonPathToken.DollarSign); // Match the '$' eagerly. This will make it optional by default.
|
context.Match(JsonPathToken.DollarSign); // Match the '$' eagerly. This will make it optional by default.
|
||||||
@ -28,7 +28,7 @@ public static partial class JsonPath
|
|||||||
if (context.Match(JsonPathToken.LeftBracket))
|
if (context.Match(JsonPathToken.LeftBracket))
|
||||||
return IndexAccessorExpression(context);
|
return IndexAccessorExpression(context);
|
||||||
|
|
||||||
throw context.SyntaxException($"Invalid expression '{context.Current.Lexeme}'.");
|
throw context.Error("Invalid expression.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IJsonPathSyntax PropertyAccessorExpression(Context context)
|
private static IJsonPathSyntax PropertyAccessorExpression(Context context)
|
||||||
@ -43,7 +43,7 @@ public static partial class JsonPath
|
|||||||
else if (context.Match(JsonPathToken.Identifier))
|
else if (context.Match(JsonPathToken.Identifier))
|
||||||
getter = new PropertySyntax(context.Previous());
|
getter = new PropertySyntax(context.Previous());
|
||||||
|
|
||||||
else throw context.SyntaxException("Expected a getter expression");
|
else throw context.Error("Expected a getter expression");
|
||||||
|
|
||||||
return new PropertyAccessorSyntax(token, getter);
|
return new PropertyAccessorSyntax(token, getter);
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ public static partial class JsonPath
|
|||||||
else if (context.Match(JsonPathToken.String))
|
else if (context.Match(JsonPathToken.String))
|
||||||
syntax = ObjectIndexExpression(token, context);
|
syntax = ObjectIndexExpression(token, context);
|
||||||
|
|
||||||
else throw context.SyntaxException("Expected an index expression.");
|
else throw context.Error("Expected an index expression.");
|
||||||
|
|
||||||
context.Consume(JsonPathToken.RightBracket, "Expected ']' after index expression.");
|
context.Consume(JsonPathToken.RightBracket, "Expected ']' after index expression.");
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ public static partial class JsonPath
|
|||||||
{
|
{
|
||||||
index = context.Consume(JsonPathToken.Number, "Invalid array index.");
|
index = context.Consume(JsonPathToken.Number, "Invalid array index.");
|
||||||
|
|
||||||
if (!int.TryParse(index.Lexeme, out _)) throw context.SyntaxException(index, "Invalid array index.");
|
if (!int.TryParse(index.Lexeme, out _)) throw context.Error(index, "Invalid array index.");
|
||||||
|
|
||||||
indexes.Add(index);
|
indexes.Add(index);
|
||||||
} while (!context.Ended() && context.Match(JsonPathToken.Comma));
|
} while (!context.Ended() && context.Match(JsonPathToken.Comma));
|
||||||
|
@ -6,6 +6,7 @@ namespace DevDisciples.Json.Tools;
|
|||||||
|
|
||||||
public static partial class JsonPath
|
public static partial class JsonPath
|
||||||
{
|
{
|
||||||
|
|
||||||
public interface IJsonPathSyntax
|
public interface IJsonPathSyntax
|
||||||
{
|
{
|
||||||
public Lexer<JsonPathToken>.Token Token { get; }
|
public Lexer<JsonPathToken>.Token Token { get; }
|
||||||
|
@ -73,7 +73,7 @@ public abstract partial class Lexer<TToken> where TToken : Enum
|
|||||||
|
|
||||||
if (src.Ended())
|
if (src.Ended())
|
||||||
{
|
{
|
||||||
throw new SyntaxException(
|
throw new ParsingException(
|
||||||
$"[line: {src.Line}, column: {src.Column}] Unterminated string near '{src.Last}'."
|
$"[line: {src.Line}, column: {src.Column}] Unterminated string near '{src.Last}'."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ public abstract partial class Lexer<TToken> where TToken : Enum
|
|||||||
|
|
||||||
if (src.Ended())
|
if (src.Ended())
|
||||||
{
|
{
|
||||||
throw new SyntaxException(
|
throw new ParsingException(
|
||||||
$"[line: {src.Line}, column: {src.Column}] Unterminated string near '{src.Last}'."
|
$"[line: {src.Line}, column: {src.Column}] Unterminated string near '{src.Last}'."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ public abstract partial class Lexer<TToken> where TToken : Enum
|
|||||||
|
|
||||||
if (!matched)
|
if (!matched)
|
||||||
{
|
{
|
||||||
Report.SyntaxHalt(ctx.Source, $"Unexpected character '{ctx.Source.Current}'.");
|
Report.Halt(ctx.Source, $"Unexpected character '{ctx.Source.Current}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,16 +86,16 @@ public class ParserContext<TToken> : ParsableStream<Lexer<TToken>.Token> where T
|
|||||||
public Lexer<TToken>.Token Consume(TToken type, string message)
|
public Lexer<TToken>.Token Consume(TToken type, string message)
|
||||||
{
|
{
|
||||||
if (Check(type)) return Advance();
|
if (Check(type)) return Advance();
|
||||||
throw SyntaxException(message);
|
throw Error(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Exception SyntaxException(string message)
|
public Exception Error(string message)
|
||||||
{
|
{
|
||||||
return new SyntaxException(Report.FormatMessage(Current, message));
|
return new ParsingException(Report.FormatMessage(Current, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Exception SyntaxException(Lexer<TToken>.Token token, string message)
|
public Exception Error(Lexer<TToken>.Token token, string message)
|
||||||
{
|
{
|
||||||
return new SyntaxException(Report.FormatMessage(token, message));
|
return new ParsingException(Report.FormatMessage(token, message));
|
||||||
}
|
}
|
||||||
}
|
}
|
16
DevDisciples.Parsing/ParsingException.cs
Normal file
16
DevDisciples.Parsing/ParsingException.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace DevDisciples.Parsing;
|
||||||
|
|
||||||
|
public class ParsingException : Exception
|
||||||
|
{
|
||||||
|
public ParsingException()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParsingException(string? message) : base(message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParsingException(string? message, Exception? innerException) : base(message, innerException)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
public static class Report
|
public static class Report
|
||||||
{
|
{
|
||||||
public static SyntaxException SyntaxException(ISourceLocation token, string message)
|
public static ParsingException Error(ISourceLocation token, string message)
|
||||||
{
|
{
|
||||||
return new SyntaxException(FormatMessage(token, message));
|
return new ParsingException(FormatMessage(token, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SyntaxHalt(ISourceLocation token, string message)
|
public static void Halt(ISourceLocation token, string message)
|
||||||
{
|
{
|
||||||
throw new SyntaxException(FormatMessage(token, message));
|
throw new ParsingException(FormatMessage(token, message));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string FormatMessage(ISourceLocation token, string msg)
|
public static string FormatMessage(ISourceLocation token, string msg)
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
namespace DevDisciples.Parsing;
|
|
||||||
|
|
||||||
public class SyntaxException : Exception
|
|
||||||
{
|
|
||||||
public SyntaxException()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyntaxException(string? message) : base(message)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public SyntaxException(string? message, Exception? innerException) : base(message, innerException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user