diff --git a/src/NJsonSchema/Generation/SampleJsonSchemaGenerator.cs b/src/NJsonSchema/Generation/SampleJsonSchemaGenerator.cs index 957d92e48..ee45247a5 100644 --- a/src/NJsonSchema/Generation/SampleJsonSchemaGenerator.cs +++ b/src/NJsonSchema/Generation/SampleJsonSchemaGenerator.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text.RegularExpressions; using Newtonsoft.Json; @@ -33,6 +34,26 @@ public JsonSchema Generate(string json) return schema; } + /// Generates the JSON Schema for the given JSON data. + /// The JSON data stream. + /// The JSON Schema. + public JsonSchema Generate(Stream stream) + { + using var reader = new StreamReader(stream); + using var jsonReader = new JsonTextReader(reader); + + var serializer = JsonSerializer.Create(new JsonSerializerSettings + { + DateFormatHandling = DateFormatHandling.IsoDateFormat + }); + + var token = serializer.Deserialize(jsonReader); + + var schema = new JsonSchema(); + Generate(token, schema, schema, "Anonymous"); + return schema; + } + private void Generate(JToken token, JsonSchema schema, JsonSchema rootSchema, string typeNameHint) { if (schema != rootSchema && token.Type == JTokenType.Object) @@ -48,7 +69,7 @@ private void Generate(JToken token, JsonSchema schema, JsonSchema rootSchema, st s.Type == JsonObjectType.Object && properties.All(p => s.Properties.ContainsKey(p.Name))); } - + if (referencedSchema == null) { referencedSchema = new JsonSchema(); diff --git a/src/NJsonSchema/Infrastructure/JsonSchemaSerialization.cs b/src/NJsonSchema/Infrastructure/JsonSchemaSerialization.cs index ff759f1fe..b3670e0a5 100644 --- a/src/NJsonSchema/Infrastructure/JsonSchemaSerialization.cs +++ b/src/NJsonSchema/Infrastructure/JsonSchemaSerialization.cs @@ -8,6 +8,7 @@ using System; +using System.IO; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; @@ -84,10 +85,10 @@ public static string ToJson(object obj, SchemaType schemaType, IContractResolver /// The contract resolver. /// The deserialized schema. [Obsolete("Use FromJsonAsync with cancellation token instead.")] - public static async Task FromJsonAsync(string json, SchemaType schemaType, string documentPath, + public static Task FromJsonAsync(string json, SchemaType schemaType, string documentPath, Func referenceResolverFactory, IContractResolver contractResolver) { - return await FromJsonAsync(json, schemaType, documentPath, referenceResolverFactory, contractResolver, CancellationToken.None).ConfigureAwait(false); + return FromJsonAsync(json, schemaType, documentPath, referenceResolverFactory, contractResolver, CancellationToken.None); } /// Deserializes JSON data to a schema with reference handling. @@ -98,29 +99,63 @@ public static async Task FromJsonAsync(string json, SchemaType schemaType, /// The contract resolver. /// The cancellation token /// The deserialized schema. - public static async Task FromJsonAsync(string json, SchemaType schemaType, string documentPath, + public static Task FromJsonAsync(string json, SchemaType schemaType, string documentPath, Func referenceResolverFactory, IContractResolver contractResolver, CancellationToken cancellationToken = default) + { + var loader = () => FromJson(json, contractResolver); + return FromJsonWithLoaderAsync(loader, schemaType, documentPath, referenceResolverFactory, contractResolver, cancellationToken); + } + + /// Deserializes JSON data to a schema with reference handling. + /// The JSON data stream. + /// The schema type. + /// The document path. + /// The reference resolver factory. + /// The contract resolver. + /// The cancellation token + /// The deserialized schema. + public static Task FromJsonAsync(Stream stream, SchemaType schemaType, string documentPath, + Func referenceResolverFactory, IContractResolver contractResolver, CancellationToken cancellationToken = default) + { + var loader = () => FromJson(stream, contractResolver); + return FromJsonWithLoaderAsync(loader, schemaType, documentPath, referenceResolverFactory, contractResolver, cancellationToken); + } + + private static async Task FromJsonWithLoaderAsync( + Func loader, + SchemaType schemaType, + string documentPath, + Func referenceResolverFactory, + IContractResolver contractResolver, + CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); CurrentSchemaType = schemaType; - var schema = FromJson(json, contractResolver); - if (schema is IDocumentPathProvider documentPathProvider) + T schema; + try { - documentPathProvider.DocumentPath = documentPath; - } + schema = loader(); + if (schema is IDocumentPathProvider documentPathProvider) + { + documentPathProvider.DocumentPath = documentPath; + } - var referenceResolver = referenceResolverFactory.Invoke(schema); - if (schema is IJsonReference referenceSchema) - { - if (!string.IsNullOrEmpty(documentPath)) + var referenceResolver = referenceResolverFactory.Invoke(schema); + if (schema is IJsonReference referenceSchema) { - referenceResolver.AddDocumentReference(documentPath, referenceSchema); + if (!string.IsNullOrEmpty(documentPath)) + { + referenceResolver.AddDocumentReference(documentPath, referenceSchema); + } } - } - await JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(schema, referenceResolver, contractResolver).ConfigureAwait(false); - CurrentSchemaType = SchemaType.JsonSchema; + await JsonSchemaReferenceUtilities.UpdateSchemaReferencesAsync(schema, referenceResolver, contractResolver, cancellationToken).ConfigureAwait(false); + } + finally + { + CurrentSchemaType = SchemaType.JsonSchema; + } return schema; } @@ -132,6 +167,44 @@ public static async Task FromJsonAsync(string json, SchemaType schemaType, public static T FromJson(string json, IContractResolver contractResolver) { IsWriting = true; + UpdateCurrentSerializerSettings(contractResolver); + + try + { + return JsonConvert.DeserializeObject(json, CurrentSerializerSettings); + } + finally + { + CurrentSerializerSettings = null; + } + } + + /// Deserializes JSON data with the given contract resolver. + /// The JSON data stream. + /// The contract resolver. + /// The deserialized schema. + public static T FromJson(Stream stream, IContractResolver contractResolver) + { + IsWriting = true; + UpdateCurrentSerializerSettings(contractResolver); + + try + { + using var reader = new StreamReader(stream); + using var jsonReader = new JsonTextReader(reader); + + var serializer = JsonSerializer.Create(CurrentSerializerSettings); + + return serializer.Deserialize(jsonReader); + } + finally + { + CurrentSerializerSettings = null; + } + } + + private static void UpdateCurrentSerializerSettings(IContractResolver contractResolver) + { CurrentSerializerSettings = new JsonSerializerSettings { ContractResolver = contractResolver, @@ -140,11 +213,6 @@ public static T FromJson(string json, IContractResolver contractResolver) ReferenceLoopHandling = ReferenceLoopHandling.Serialize, PreserveReferencesHandling = PreserveReferencesHandling.None }; - - var obj = JsonConvert.DeserializeObject(json, CurrentSerializerSettings); - CurrentSerializerSettings = null; - - return obj; } } } diff --git a/src/NJsonSchema/JsonSchema.cs b/src/NJsonSchema/JsonSchema.cs index b4e327ccb..bb7c2a10d 100644 --- a/src/NJsonSchema/JsonSchema.cs +++ b/src/NJsonSchema/JsonSchema.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; @@ -137,14 +138,22 @@ public static JsonSchema FromSampleJson(string data) return generator.Generate(data); } + /// Creates a from sample JSON data. + /// The JSON Schema. + public static JsonSchema FromSampleJson(Stream stream) + { + var generator = new SampleJsonSchemaGenerator(); + return generator.Generate(stream); + } + /// Loads a JSON Schema from a given file path (only available in .NET 4.x). /// The file path. /// Cancellation token instance /// The JSON Schema. - public static async Task FromFileAsync(string filePath, CancellationToken cancellationToken = default) + public static Task FromFileAsync(string filePath, CancellationToken cancellationToken = default) { var factory = JsonReferenceResolver.CreateJsonReferenceResolverFactory(new DefaultTypeNameGenerator()); - return await FromFileAsync(filePath, factory, cancellationToken).ConfigureAwait(false); + return FromFileAsync(filePath, factory, cancellationToken); } /// Loads a JSON Schema from a given file path (only available in .NET 4.x). @@ -153,10 +162,15 @@ public static async Task FromFileAsync(string filePath, Cancellation /// The cancellation token /// The JSON Schema. /// The System.IO.File API is not available on this platform. - public static async Task FromFileAsync(string filePath, Func referenceResolverFactory, CancellationToken cancellationToken = default) + public static Task FromFileAsync(string filePath, Func referenceResolverFactory, CancellationToken cancellationToken = default) { - var data = DynamicApis.FileReadAllText(filePath); - return await FromJsonAsync(data, filePath, referenceResolverFactory, cancellationToken).ConfigureAwait(false); +#if !NETSTANDARD1_0 + using var stream = File.OpenRead(filePath); + return FromJsonAsync(stream, filePath, referenceResolverFactory, cancellationToken); +#else + var json = DynamicApis.FileReadAllText(filePath); + return FromJsonAsync(json, filePath, referenceResolverFactory, cancellationToken); +#endif } /// Loads a JSON Schema from a given URL (only available in .NET 4.x). @@ -164,10 +178,10 @@ public static async Task FromFileAsync(string filePath, FuncThe cancellation token /// The JSON Schema. /// The HttpClient.GetAsync API is not available on this platform. - public static async Task FromUrlAsync(string url, CancellationToken cancellationToken = default) + public static Task FromUrlAsync(string url, CancellationToken cancellationToken = default) { var factory = JsonReferenceResolver.CreateJsonReferenceResolverFactory(new DefaultTypeNameGenerator()); - return await FromUrlAsync(url, factory, cancellationToken).ConfigureAwait(false); + return FromUrlAsync(url, factory, cancellationToken); } /// Loads a JSON Schema from a given URL (only available in .NET 4.x). @@ -186,9 +200,19 @@ public static async Task FromUrlAsync(string url, FuncThe JSON string. /// The cancellation token /// The JSON Schema. - public static async Task FromJsonAsync(string data, CancellationToken cancellationToken = default) + public static Task FromJsonAsync(string data, CancellationToken cancellationToken = default) { - return await FromJsonAsync(data, null, cancellationToken).ConfigureAwait(false); + return FromJsonAsync(data, null, cancellationToken); + } + + /// Deserializes a JSON stream to a . + /// The JSON data stream. + /// The cancellation token + /// The JSON Schema. + public static Task FromJsonAsync(Stream stream, CancellationToken cancellationToken = default) + { + var factory = JsonReferenceResolver.CreateJsonReferenceResolverFactory(new DefaultTypeNameGenerator()); + return FromJsonAsync(stream, null, factory, cancellationToken); } /// Deserializes a JSON string to a . @@ -196,10 +220,10 @@ public static async Task FromJsonAsync(string data, CancellationToke /// The document path (URL or file path) for resolving relative document references. /// The cancellation token /// The JSON Schema. - public static async Task FromJsonAsync(string data, string documentPath, CancellationToken cancellationToken = default) + public static Task FromJsonAsync(string data, string documentPath, CancellationToken cancellationToken = default) { var factory = JsonReferenceResolver.CreateJsonReferenceResolverFactory(new DefaultTypeNameGenerator()); - return await FromJsonAsync(data, documentPath, factory, cancellationToken).ConfigureAwait(false); + return FromJsonAsync(data, documentPath, factory, cancellationToken); } /// Deserializes a JSON string to a . @@ -208,10 +232,22 @@ public static async Task FromJsonAsync(string data, string documentP /// The JSON reference resolver factory. /// The cancellation token /// The JSON Schema. - public static async Task FromJsonAsync(string data, string documentPath, Func FromJsonAsync(string data, string documentPath, Func referenceResolverFactory, CancellationToken cancellationToken = default) + { + return JsonSchemaSerialization.FromJsonAsync(data, SerializationSchemaType, documentPath, referenceResolverFactory, ContractResolver.Value, cancellationToken); + } + + /// Deserializes a JSON string to a . + /// The JSON data stream. + /// The document path (URL or file path) for resolving relative document references. + /// The JSON reference resolver factory. + /// The cancellation token + /// The JSON Schema. + public static Task FromJsonAsync(Stream stream, string documentPath, Func referenceResolverFactory, CancellationToken cancellationToken = default) { - return await JsonSchemaSerialization.FromJsonAsync(data, SerializationSchemaType, documentPath, referenceResolverFactory, ContractResolver.Value, cancellationToken).ConfigureAwait(false); + return JsonSchemaSerialization.FromJsonAsync(stream, SerializationSchemaType, documentPath, referenceResolverFactory, ContractResolver.Value, cancellationToken); } internal static JsonSchema FromJsonWithCurrentSettings(object obj)