diff --git a/IOpenApiPrimitive.cs b/IOpenApiPrimitive.cs
new file mode 100644
index 000000000..d367b0c75
--- /dev/null
+++ b/IOpenApiPrimitive.cs
@@ -0,0 +1,82 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+namespace Microsoft.OpenApi.Any
+{
+ ///
+ /// Primitive type.
+ ///
+ public enum PrimitiveType
+ {
+ ///
+ /// Integer
+ ///
+ Integer,
+
+ ///
+ /// Long
+ ///
+ Long,
+
+ ///
+ /// Float
+ ///
+ Float,
+
+ ///
+ /// Double
+ ///
+ Double,
+
+ ///
+ /// String
+ ///
+ String,
+
+ ///
+ /// Byte
+ ///
+ Byte,
+
+ ///
+ /// Binary
+ ///
+ Binary,
+
+ ///
+ /// Boolean
+ ///
+ Boolean,
+
+ ///
+ /// Date
+ ///
+ Date,
+
+ ///
+ /// DateTime
+ ///
+ DateTime,
+
+ ///
+ /// Time
+ ///
+ TimeSpan,
+
+ ///
+ /// Password
+ ///
+ Password
+ }
+
+ ///
+ /// Base interface for the Primitive type.
+ ///
+ public interface IOpenApiPrimitive : IOpenApiAny
+ {
+ ///
+ /// Primitive type.
+ ///
+ PrimitiveType PrimitiveType { get; }
+ }
+}
diff --git a/OpenApiAnyConverter.cs b/OpenApiAnyConverter.cs
new file mode 100644
index 000000000..2aa283a4c
--- /dev/null
+++ b/OpenApiAnyConverter.cs
@@ -0,0 +1,284 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+
+namespace Microsoft.OpenApi.Readers.ParseNodes
+{
+ internal static class OpenApiAnyConverter
+ {
+ ///
+ /// Converts the s in the given
+ /// into the appropriate type based on the given .
+ /// For those strings that the schema does not specify the type for, convert them into
+ /// the most specific type based on the value.
+ ///
+ public static IOpenApiAny GetSpecificOpenApiAny(IOpenApiAny openApiAny, OpenApiSchema schema = null)
+ {
+ if (openApiAny is OpenApiArray openApiArray)
+ {
+ var newArray = new OpenApiArray();
+ foreach (var element in openApiArray)
+ {
+ newArray.Add(GetSpecificOpenApiAny(element, schema?.Items));
+ }
+
+ return newArray;
+ }
+
+ if (openApiAny is OpenApiObject openApiObject)
+ {
+ var newObject = new OpenApiObject();
+
+ foreach (var key in openApiObject.Keys.ToList())
+ {
+ if (schema?.Properties != null && schema.Properties.TryGetValue(key, out var property))
+ {
+ newObject[key] = GetSpecificOpenApiAny(openApiObject[key], property);
+ }
+ else
+ {
+ newObject[key] = GetSpecificOpenApiAny(openApiObject[key], schema?.AdditionalProperties);
+ }
+ }
+
+ return newObject;
+ }
+
+ if (!(openApiAny is OpenApiString))
+ {
+ return openApiAny;
+ }
+
+ var value = ((OpenApiString)openApiAny).Value;
+ var type = schema?.Type;
+ var format = schema?.Format;
+
+ if (((OpenApiString)openApiAny).IsExplicit())
+ {
+ // More narrow type detection for explicit strings, only check types that are passed as strings
+ if (schema == null)
+ {
+ if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue))
+ {
+ return new OpenApiDateTime(dateTimeValue);
+ }
+ }
+ else if (type == "string")
+ {
+ if (format == "byte")
+ {
+ try
+ {
+ return new OpenApiByte(Convert.FromBase64String(value));
+ }
+ catch (FormatException)
+ { }
+ }
+
+ if (format == "binary")
+ {
+ try
+ {
+ return new OpenApiBinary(Encoding.UTF8.GetBytes(value));
+ }
+ catch (EncoderFallbackException)
+ { }
+ }
+
+ if (format == "date")
+ {
+ if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue))
+ {
+ return new OpenApiDate(dateValue.Date);
+ }
+ }
+
+ if (format == "date-time")
+ {
+ if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue))
+ {
+ return new OpenApiDateTime(dateTimeValue);
+ }
+ }
+
+ if (format == "date-span")
+ {
+ if (TimeSpan.TryParse(value, out var timeSpanValue))
+ {
+ return new OpenApiTimeSpan(timeSpanValue);
+ }
+ }
+
+ if (format == "password")
+ {
+ return new OpenApiPassword(value);
+ }
+ }
+
+ return openApiAny;
+ }
+
+ if (value == null || value == "null")
+ {
+ return new OpenApiNull();
+ }
+
+ if (schema?.Type == null)
+ {
+ if (value == "true")
+ {
+ return new OpenApiBoolean(true);
+ }
+
+ if (value == "false")
+ {
+ return new OpenApiBoolean(false);
+ }
+
+ if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ {
+ return new OpenApiInteger(intValue);
+ }
+
+ if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue))
+ {
+ return new OpenApiLong(longValue);
+ }
+
+ if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue))
+ {
+ return new OpenApiDouble(doubleValue);
+ }
+
+ if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue))
+ {
+ return new OpenApiDateTime(dateTimeValue);
+ }
+ }
+ else
+ {
+ if (type == "integer" && format == "int32")
+ {
+ if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ {
+ return new OpenApiInteger(intValue);
+ }
+ }
+
+ if (type == "integer" && format == "int64")
+ {
+ if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue))
+ {
+ return new OpenApiLong(longValue);
+ }
+ }
+
+ if (type == "integer")
+ {
+ if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
+ {
+ return new OpenApiInteger(intValue);
+ }
+ }
+
+ if (type == "number" && format == "float")
+ {
+ if (float.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var floatValue))
+ {
+ return new OpenApiFloat(floatValue);
+ }
+ }
+
+ if (type == "number" && format == "double")
+ {
+ if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue))
+ {
+ return new OpenApiDouble(doubleValue);
+ }
+ }
+
+ if (type == "number")
+ {
+ if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue))
+ {
+ return new OpenApiDouble(doubleValue);
+ }
+ }
+
+ if (type == "string" && format == "byte")
+ {
+ try
+ {
+ return new OpenApiByte(Convert.FromBase64String(value));
+ }
+ catch (FormatException)
+ { }
+ }
+
+ // binary
+ if (type == "string" && format == "binary")
+ {
+ try
+ {
+ return new OpenApiBinary(Encoding.UTF8.GetBytes(value));
+ }
+ catch (EncoderFallbackException)
+ { }
+ }
+
+ if (type == "string" && format == "date")
+ {
+ if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue))
+ {
+ return new OpenApiDate(dateValue.Date);
+ }
+ }
+
+ if (type == "string" && format == "date-time")
+ {
+ if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue))
+ {
+ return new OpenApiDateTime(dateTimeValue);
+ }
+ }
+
+ if (type == "string" && format == "date-span")
+ {
+ if (TimeSpan.TryParse(value, out var timeSpanValue))
+ {
+ return new OpenApiTimeSpan(timeSpanValue);
+ }
+ }
+
+ if (type == "string" && format == "password")
+ {
+ return new OpenApiPassword(value);
+ }
+
+ if (type == "string")
+ {
+ return openApiAny;
+ }
+
+ if (type == "boolean")
+ {
+ if (bool.TryParse(value, out var booleanValue))
+ {
+ return new OpenApiBoolean(booleanValue);
+ }
+ }
+ }
+
+ // If data conflicts with the given type, return a string.
+ // This converter is used in the parser, so it does not perform any validations,
+ // but the validator can be used to validate whether the data and given type conflicts.
+ return openApiAny;
+ }
+ }
+}
diff --git a/OpenApiAnyConverterTests.cs b/OpenApiAnyConverterTests.cs
new file mode 100644
index 000000000..4c9424642
--- /dev/null
+++ b/OpenApiAnyConverterTests.cs
@@ -0,0 +1,524 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using FluentAssertions;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers.ParseNodes;
+using SharpYaml.Serialization;
+using Xunit;
+
+namespace Microsoft.OpenApi.Readers.Tests.ParseNodes
+{
+ [Collection("DefaultSettings")]
+ public class OpenApiAnyConverterTests
+ {
+ [Fact]
+ public void ParseObjectAsAnyShouldSucceed()
+ {
+ var input = @"
+aString: fooBar
+aInteger: 10
+aDouble: 2.34
+aDateTime: 2017-01-01
+aTimeSpan: 01:00:00
+aDate: 2017-01-02
+ ";
+ var yamlStream = new YamlStream();
+ yamlStream.Load(new StringReader(input));
+ var yamlNode = yamlStream.Documents.First().RootNode;
+
+ var diagnostic = new OpenApiDiagnostic();
+ var context = new ParsingContext(diagnostic);
+
+ var node = new MapNode(context, (YamlMappingNode)yamlNode);
+
+ var anyMap = node.CreateAny();
+
+ var schema = new OpenApiSchema()
+ {
+ Type = "object",
+ Properties =
+ {
+ ["aString"] = new OpenApiSchema()
+ {
+ Type = "string"
+ },
+ ["aInteger"] = new OpenApiSchema()
+ {
+ Type = "integer",
+ Format = "int32"
+ },
+ ["aDouble"] = new OpenApiSchema()
+ {
+ Type = "number",
+ Format = "double"
+ },
+ ["aDateTime"] = new OpenApiSchema()
+ {
+ Type = "string",
+ Format = "date-time"
+ },
+ ["aTimeSpan"] = new OpenApiSchema()
+ {
+ Type = "string",
+ Format = "date-span"
+ },
+ ["aDate"] = new OpenApiSchema()
+ {
+ Type = "string",
+ Format = "date"
+ }
+ }
+ };
+
+ anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap, schema);
+
+ diagnostic.Errors.Should().BeEmpty();
+
+ anyMap.Should().BeEquivalentTo(
+ new OpenApiObject
+ {
+ ["aString"] = new OpenApiString("fooBar"),
+ ["aInteger"] = new OpenApiInteger(10),
+ ["aDouble"] = new OpenApiDouble(2.34),
+ ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture)),
+ ["aTimeSpan"] = new OpenApiTimeSpan(TimeSpan.FromHours(1)),
+ ["aDate"] = new OpenApiDate(DateTimeOffset.Parse("2017-01-02", CultureInfo.InvariantCulture).Date),
+ });
+ }
+
+
+ [Fact]
+ public void ParseNestedObjectAsAnyShouldSucceed()
+ {
+ var input = @"
+ aString: fooBar
+ aInteger: 10
+ aArray:
+ - 1
+ - 2
+ - 3
+ aNestedArray:
+ - aFloat: 1
+ aPassword: 1234
+ aArray: [abc, def]
+ aDictionary:
+ arbitraryProperty: 1
+ arbitraryProperty2: 2
+ - aFloat: 1.6
+ aArray: [123]
+ aDictionary:
+ arbitraryProperty: 1
+ arbitraryProperty3: 20
+ aObject:
+ aDate: 2017-02-03
+ aDouble: 2.34
+ aDateTime: 2017-01-01
+ ";
+ var yamlStream = new YamlStream();
+ yamlStream.Load(new StringReader(input));
+ var yamlNode = yamlStream.Documents.First().RootNode;
+
+ var diagnostic = new OpenApiDiagnostic();
+ var context = new ParsingContext(diagnostic);
+
+ var node = new MapNode(context, (YamlMappingNode)yamlNode);
+
+ var anyMap = node.CreateAny();
+
+ var schema = new OpenApiSchema()
+ {
+ Type = "object",
+ Properties =
+ {
+ ["aString"] = new OpenApiSchema()
+ {
+ Type = "string"
+ },
+ ["aInteger"] = new OpenApiSchema()
+ {
+ Type = "integer",
+ Format = "int32"
+ },
+ ["aArray"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Items = new OpenApiSchema()
+ {
+ Type = "integer",
+ Format = "int64"
+ }
+ },
+ ["aNestedArray"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Items = new OpenApiSchema()
+ {
+ Type = "object",
+ Properties =
+ {
+ ["aFloat"] = new OpenApiSchema()
+ {
+ Type = "number",
+ Format = "float"
+ },
+ ["aPassword"] = new OpenApiSchema()
+ {
+ Type = "string",
+ Format = "password"
+ },
+ ["aArray"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Items = new OpenApiSchema()
+ {
+ Type = "string",
+ }
+ },
+ ["aDictionary"] = new OpenApiSchema()
+ {
+ Type = "object",
+ AdditionalProperties = new OpenApiSchema()
+ {
+ Type = "integer",
+ Format = "int64"
+ }
+ }
+ }
+ }
+ },
+ ["aObject"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Properties =
+ {
+ ["aDate"] = new OpenApiSchema()
+ {
+ Type = "string",
+ Format = "date"
+ }
+ }
+ },
+ ["aDouble"] = new OpenApiSchema()
+ {
+ Type = "number",
+ Format = "double"
+ },
+ ["aDateTime"] = new OpenApiSchema()
+ {
+ Type = "string",
+ Format = "date-time"
+ }
+ }
+ };
+
+ anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap, schema);
+
+ diagnostic.Errors.Should().BeEmpty();
+
+ anyMap.Should().BeEquivalentTo(
+ new OpenApiObject
+ {
+ ["aString"] = new OpenApiString("fooBar"),
+ ["aInteger"] = new OpenApiInteger(10),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiLong(1),
+ new OpenApiLong(2),
+ new OpenApiLong(3),
+ },
+ ["aNestedArray"] = new OpenApiArray()
+ {
+ new OpenApiObject()
+ {
+ ["aFloat"] = new OpenApiFloat(1),
+ ["aPassword"] = new OpenApiPassword("1234"),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiString("abc"),
+ new OpenApiString("def")
+ },
+ ["aDictionary"] = new OpenApiObject()
+ {
+ ["arbitraryProperty"] = new OpenApiLong(1),
+ ["arbitraryProperty2"] = new OpenApiLong(2),
+ }
+ },
+ new OpenApiObject()
+ {
+ ["aFloat"] = new OpenApiFloat((float)1.6),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiString("123"),
+ },
+ ["aDictionary"] = new OpenApiObject()
+ {
+ ["arbitraryProperty"] = new OpenApiLong(1),
+ ["arbitraryProperty3"] = new OpenApiLong(20),
+ }
+ }
+ },
+ ["aObject"] = new OpenApiObject()
+ {
+ ["aDate"] = new OpenApiDate(DateTimeOffset.Parse("2017-02-03", CultureInfo.InvariantCulture).Date)
+ },
+ ["aDouble"] = new OpenApiDouble(2.34),
+ ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture))
+ });
+ }
+
+
+ [Fact]
+ public void ParseNestedObjectAsAnyWithPartialSchemaShouldSucceed()
+ {
+ var input = @"
+ aString: fooBar
+ aInteger: 10
+ aArray:
+ - 1
+ - 2
+ - 3
+ aNestedArray:
+ - aFloat: 1
+ aPassword: 1234
+ aArray: [abc, def]
+ aDictionary:
+ arbitraryProperty: 1
+ arbitraryProperty2: 2
+ - aFloat: 1.6
+ aArray: [123]
+ aDictionary:
+ arbitraryProperty: 1
+ arbitraryProperty3: 20
+ aObject:
+ aDate: 2017-02-03
+ aDouble: 2.34
+ aDateTime: 2017-01-01
+ ";
+ var yamlStream = new YamlStream();
+ yamlStream.Load(new StringReader(input));
+ var yamlNode = yamlStream.Documents.First().RootNode;
+
+ var diagnostic = new OpenApiDiagnostic();
+ var context = new ParsingContext(diagnostic);
+
+ var node = new MapNode(context, (YamlMappingNode)yamlNode);
+
+ var anyMap = node.CreateAny();
+
+ var schema = new OpenApiSchema()
+ {
+ Type = "object",
+ Properties =
+ {
+ ["aString"] = new OpenApiSchema()
+ {
+ Type = "string"
+ },
+ ["aArray"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Items = new OpenApiSchema()
+ {
+ Type = "integer"
+ }
+ },
+ ["aNestedArray"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Items = new OpenApiSchema()
+ {
+ Type = "object",
+ Properties =
+ {
+ ["aFloat"] = new OpenApiSchema()
+ {
+ },
+ ["aPassword"] = new OpenApiSchema()
+ {
+ },
+ ["aArray"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Items = new OpenApiSchema()
+ {
+ Type = "string",
+ }
+ }
+ }
+ }
+ },
+ ["aObject"] = new OpenApiSchema()
+ {
+ Type = "array",
+ Properties =
+ {
+ ["aDate"] = new OpenApiSchema()
+ {
+ Type = "string"
+ }
+ }
+ },
+ ["aDouble"] = new OpenApiSchema()
+ {
+ },
+ ["aDateTime"] = new OpenApiSchema()
+ {
+ }
+ }
+ };
+
+ anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap, schema);
+
+ diagnostic.Errors.Should().BeEmpty();
+
+ anyMap.Should().BeEquivalentTo(
+ new OpenApiObject
+ {
+ ["aString"] = new OpenApiString("fooBar"),
+ ["aInteger"] = new OpenApiInteger(10),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiInteger(1),
+ new OpenApiInteger(2),
+ new OpenApiInteger(3),
+ },
+ ["aNestedArray"] = new OpenApiArray()
+ {
+ new OpenApiObject()
+ {
+ ["aFloat"] = new OpenApiInteger(1),
+ ["aPassword"] = new OpenApiInteger(1234),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiString("abc"),
+ new OpenApiString("def")
+ },
+ ["aDictionary"] = new OpenApiObject()
+ {
+ ["arbitraryProperty"] = new OpenApiInteger(1),
+ ["arbitraryProperty2"] = new OpenApiInteger(2),
+ }
+ },
+ new OpenApiObject()
+ {
+ ["aFloat"] = new OpenApiDouble(1.6),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiString("123"),
+ },
+ ["aDictionary"] = new OpenApiObject()
+ {
+ ["arbitraryProperty"] = new OpenApiInteger(1),
+ ["arbitraryProperty3"] = new OpenApiInteger(20),
+ }
+ }
+ },
+ ["aObject"] = new OpenApiObject()
+ {
+ ["aDate"] = new OpenApiString("2017-02-03")
+ },
+ ["aDouble"] = new OpenApiDouble(2.34),
+ ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture))
+ });
+ }
+
+ [Fact]
+ public void ParseNestedObjectAsAnyWithoutUsingSchemaShouldSucceed()
+ {
+ var input = @"
+ aString: fooBar
+ aInteger: 10
+ aArray:
+ - 1
+ - 2
+ - 3
+ aNestedArray:
+ - aFloat: 1
+ aPassword: 1234
+ aArray: [abc, def]
+ aDictionary:
+ arbitraryProperty: 1
+ arbitraryProperty2: 2
+ - aFloat: 1.6
+ aArray: [123]
+ aDictionary:
+ arbitraryProperty: 1
+ arbitraryProperty3: 20
+ aObject:
+ aDate: 2017-02-03
+ aDouble: 2.34
+ aDateTime: 2017-01-01
+ ";
+ var yamlStream = new YamlStream();
+ yamlStream.Load(new StringReader(input));
+ var yamlNode = yamlStream.Documents.First().RootNode;
+
+ var diagnostic = new OpenApiDiagnostic();
+ var context = new ParsingContext(diagnostic);
+
+ var node = new MapNode(context, (YamlMappingNode)yamlNode);
+
+ var anyMap = node.CreateAny();
+
+ anyMap = OpenApiAnyConverter.GetSpecificOpenApiAny(anyMap);
+
+ diagnostic.Errors.Should().BeEmpty();
+
+ anyMap.Should().BeEquivalentTo(
+ new OpenApiObject
+ {
+ ["aString"] = new OpenApiString("fooBar"),
+ ["aInteger"] = new OpenApiInteger(10),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiInteger(1),
+ new OpenApiInteger(2),
+ new OpenApiInteger(3),
+ },
+ ["aNestedArray"] = new OpenApiArray()
+ {
+ new OpenApiObject()
+ {
+ ["aFloat"] = new OpenApiInteger(1),
+ ["aPassword"] = new OpenApiInteger(1234),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiString("abc"),
+ new OpenApiString("def")
+ },
+ ["aDictionary"] = new OpenApiObject()
+ {
+ ["arbitraryProperty"] = new OpenApiInteger(1),
+ ["arbitraryProperty2"] = new OpenApiInteger(2),
+ }
+ },
+ new OpenApiObject()
+ {
+ ["aFloat"] = new OpenApiDouble(1.6),
+ ["aArray"] = new OpenApiArray()
+ {
+ new OpenApiInteger(123),
+ },
+ ["aDictionary"] = new OpenApiObject()
+ {
+ ["arbitraryProperty"] = new OpenApiInteger(1),
+ ["arbitraryProperty3"] = new OpenApiInteger(20),
+ }
+ }
+ },
+ ["aObject"] = new OpenApiObject()
+ {
+ ["aDate"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-02-03", CultureInfo.InvariantCulture))
+ },
+ ["aDouble"] = new OpenApiDouble(2.34),
+ ["aDateTime"] = new OpenApiDateTime(DateTimeOffset.Parse("2017-01-01", CultureInfo.InvariantCulture))
+ });
+ }
+ }
+}
diff --git a/OpenApiPrimitive.cs b/OpenApiPrimitive.cs
new file mode 100644
index 000000000..8439262f6
--- /dev/null
+++ b/OpenApiPrimitive.cs
@@ -0,0 +1,148 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license.
+
+using System;
+using System.Text;
+using Microsoft.OpenApi.Exceptions;
+using Microsoft.OpenApi.Properties;
+using Microsoft.OpenApi.Writers;
+
+namespace Microsoft.OpenApi.Any
+{
+ ///
+ /// Open API primitive class.
+ ///
+ ///
+ public abstract class OpenApiPrimitive : IOpenApiPrimitive
+ {
+ ///
+ /// Initializes the class with the given value.
+ ///
+ ///
+ public OpenApiPrimitive(T value)
+ {
+ Value = value;
+ }
+
+ ///
+ /// Initializes a copy of an object
+ ///
+ ///
+ public OpenApiPrimitive(OpenApiPrimitive openApiPrimitive)
+ {
+ Value = openApiPrimitive.Value;
+ }
+
+ ///
+ /// The kind of .
+ ///
+ public AnyType AnyType { get; } = AnyType.Primitive;
+
+ ///
+ /// The primitive class this object represents.
+ ///
+ public abstract PrimitiveType PrimitiveType { get; }
+
+ ///
+ /// Value of this
+ ///
+ public T Value { get; }
+
+ ///
+ /// Write out content of primitive element
+ ///
+ ///
+ ///
+ public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion)
+ {
+ switch (this.PrimitiveType)
+ {
+ case PrimitiveType.Integer:
+ var intValue = (OpenApiInteger)(IOpenApiPrimitive)this;
+ writer.WriteValue(intValue.Value);
+ break;
+
+ case PrimitiveType.Long:
+ var longValue = (OpenApiLong)(IOpenApiPrimitive)this;
+ writer.WriteValue(longValue.Value);
+ break;
+
+ case PrimitiveType.Float:
+ var floatValue = (OpenApiFloat)(IOpenApiPrimitive)this;
+ writer.WriteValue(floatValue.Value);
+ break;
+
+ case PrimitiveType.Double:
+ var doubleValue = (OpenApiDouble)(IOpenApiPrimitive)this;
+ writer.WriteValue(doubleValue.Value);
+ break;
+
+ case PrimitiveType.String:
+ var stringValue = (OpenApiString)(IOpenApiPrimitive)this;
+ if (stringValue.IsRawString())
+ writer.WriteRaw(stringValue.Value);
+ else
+ writer.WriteValue(stringValue.Value);
+ break;
+
+ case PrimitiveType.Byte:
+ var byteValue = (OpenApiByte)(IOpenApiPrimitive)this;
+ if (byteValue.Value == null)
+ {
+ writer.WriteNull();
+ }
+ else
+ {
+ writer.WriteValue(Convert.ToBase64String(byteValue.Value));
+ }
+
+ break;
+
+ case PrimitiveType.Binary:
+ var binaryValue = (OpenApiBinary)(IOpenApiPrimitive)this;
+ if (binaryValue.Value == null)
+ {
+ writer.WriteNull();
+ }
+ else
+ {
+ writer.WriteValue(Encoding.UTF8.GetString(binaryValue.Value));
+ }
+
+ break;
+
+ case PrimitiveType.Boolean:
+ var boolValue = (OpenApiBoolean)(IOpenApiPrimitive)this;
+ writer.WriteValue(boolValue.Value);
+ break;
+
+ case PrimitiveType.Date:
+ var dateValue = (OpenApiDate)(IOpenApiPrimitive)this;
+ writer.WriteValue(dateValue.Value);
+ break;
+
+ case PrimitiveType.DateTime:
+ var dateTimeValue = (OpenApiDateTime)(IOpenApiPrimitive)this;
+ writer.WriteValue(dateTimeValue.Value);
+ break;
+
+ case PrimitiveType.TimeSpan:
+ var timeSpanValue = (OpenApiTimeSpan)(IOpenApiPrimitive)this;
+ writer.WriteValue(timeSpanValue.Value);
+ break;
+
+ case PrimitiveType.Password:
+ var passwordValue = (OpenApiPassword)(IOpenApiPrimitive)this;
+ writer.WriteValue(passwordValue.Value);
+ break;
+
+ default:
+ throw new OpenApiWriterException(
+ string.Format(
+ SRResource.PrimitiveTypeNotSupported,
+ this.PrimitiveType));
+ }
+
+ }
+ }
+}
diff --git a/OpenApiTimeSpan.cs b/OpenApiTimeSpan.cs
new file mode 100644
index 000000000..c807a6a01
--- /dev/null
+++ b/OpenApiTimeSpan.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Microsoft.OpenApi.Any
+{
+ ///
+ /// Open API TimeSpan
+ ///
+ public class OpenApiTimeSpan : OpenApiPrimitive
+ {
+ ///
+ /// Initializes the class.
+ ///
+ public OpenApiTimeSpan(TimeSpan value)
+ : base(value)
+ {
+ }
+
+ ///
+ /// Primitive type this object represents.
+ ///
+ public override PrimitiveType PrimitiveType { get; } = PrimitiveType.TimeSpan;
+ }
+}