diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..ef927836
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "dotnet.defaultSolution": "ApiDoctor.sln"
+}
diff --git a/ApiDoctor.Validation.UnitTests/YamlParserTests.cs b/ApiDoctor.Validation.UnitTests/YamlParserTests.cs
new file mode 100644
index 00000000..96c4a11d
--- /dev/null
+++ b/ApiDoctor.Validation.UnitTests/YamlParserTests.cs
@@ -0,0 +1,91 @@
+/*
+ * API Doctor
+ * Copyright (c) Microsoft Corporation
+ * All rights reserved.
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the ""Software""), to deal in
+ * the Software without restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
+ * Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+namespace ApiDoctor.Validation.UnitTests
+{
+ using System.Linq;
+ using ApiDoctor.Validation;
+ using ApiDoctor.Validation.Error;
+ using NUnit.Framework;
+
+ [TestFixture]
+ public class YamlParserTests
+ {
+ private static readonly string yamlWithMultiLineArray = @"title: ""Define the /me as singleton""
+description: ""These are things I had to add in the docs to make sure the Markdown-Scanner""
+ms.localizationpriority: medium
+author: """"
+ms.prod: """"
+doc_type: conceptualPageType
+toc.keywords:
+- foo
+- bar
+";
+
+ // Missing closing double-quote on title property
+ private static readonly string malformedYaml = @"title: ""Define the /me as singleton
+description: ""These are things I had to add in the docs to make sure the Markdown-Scanner""
+ms.localizationpriority: medium
+author: """"
+ms.prod: """"
+doc_type: conceptualPageType
+toc.keywords:
+- foo
+- bar
+";
+
+ [Test]
+ public void YamlWithMultiLineArrayParses()
+ {
+ // Arrange
+ _ = new DocSet();
+ var issues = new IssueLogger();
+
+ // Act
+ DocFile.ParseYamlMetadata(yamlWithMultiLineArray, issues);
+
+ // Assert
+ Assert.That(!issues.Issues.WereErrors());
+ }
+
+ [Test]
+ public void MalformedYamlGeneratesError()
+ {
+ // Arrange
+ _ = new DocSet();
+ var issues = new IssueLogger();
+
+ // Act
+ DocFile.ParseYamlMetadata(malformedYaml, issues);
+
+ // Assert
+ Assert.That(issues.Issues.WereErrors());
+ var error = issues.Issues.FirstOrDefault();
+ Assert.That(error != null);
+ Assert.That(error.IsError);
+ Assert.That(error.Message == "Incorrect YAML header format");
+ }
+ }
+}
\ No newline at end of file
diff --git a/ApiDoctor.Validation/ApiDoctor.Validation.csproj b/ApiDoctor.Validation/ApiDoctor.Validation.csproj
index 5b3f4717..52d6c774 100644
--- a/ApiDoctor.Validation/ApiDoctor.Validation.csproj
+++ b/ApiDoctor.Validation/ApiDoctor.Validation.csproj
@@ -29,5 +29,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/ApiDoctor.Validation/DocFile.cs b/ApiDoctor.Validation/DocFile.cs
index 56e8c912..64bc8a11 100644
--- a/ApiDoctor.Validation/DocFile.cs
+++ b/ApiDoctor.Validation/DocFile.cs
@@ -1,25 +1,25 @@
/*
* API Doctor
* Copyright (c) Microsoft Corporation
-* All rights reserved.
-*
+* All rights reserved.
+*
* MIT License
-*
-* Permission is hereby granted, free of charge, to any person obtaining a copy of
-* this software and associated documentation files (the ""Software""), to deal in
-* the Software without restriction, including without limitation the rights to use,
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy of
+* this software and associated documentation files (the ""Software""), to deal in
+* the Software without restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
-* Software, and to permit persons to whom the Software is furnished to do so,
+* Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
-*
-* The above copyright notice and this permission notice shall be included in all
+*
+* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
-*
-* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+*
+* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@@ -30,12 +30,13 @@ namespace ApiDoctor.Validation
using System.Diagnostics;
using System.IO;
using System.Linq;
+ using ApiDoctor.Validation.Config;
using ApiDoctor.Validation.Error;
using ApiDoctor.Validation.TableSpec;
using Tags;
using MarkdownDeep;
using Newtonsoft.Json;
- using ApiDoctor.Validation.Config;
+ using YamlDotNet.Serialization;
///
/// A documentation file that may contain one more resources or API methods
@@ -50,6 +51,8 @@ public partial class DocFile
private readonly List enums = new List();
private readonly List bookmarks = new List();
+ private static readonly IDeserializer yamlDeserializer = new DeserializerBuilder().Build();
+
protected bool HasScanRun;
protected string BasePath;
@@ -389,29 +392,23 @@ internal static (string YamlFrontMatter, string ProcessedContent) ParseAndRemove
internal static void ParseYamlMetadata(string yamlMetadata, IssueLogger issues)
{
- Dictionary dictionary = new Dictionary();
- string[] items = yamlMetadata.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
- foreach (string item in items)
+ Dictionary dictionary = null;
+ try
{
- try
- {
- string[] keyValue = item.Split(':');
- dictionary.Add(keyValue[0].Trim(), keyValue[1].Trim());
- }
- catch (Exception)
- {
- issues.Error(ValidationErrorCode.IncorrectYamlHeaderFormat, $"Incorrect YAML header format after `{dictionary.Keys.Last()}`");
- }
+ dictionary = yamlDeserializer.Deserialize>(yamlMetadata);
+ }
+ catch (Exception)
+ {
+ issues.Error(ValidationErrorCode.IncorrectYamlHeaderFormat, "Incorrect YAML header format");
}
List missingHeaders = new List();
foreach (var header in DocSet.SchemaConfig.RequiredYamlHeaders)
{
- string value;
- if (dictionary.TryGetValue(header, out value))
+ if (dictionary.TryGetValue(header, out object value) && value is string stringValue)
{
- value = value.Replace("\"", string.Empty);
- if (string.IsNullOrWhiteSpace(value))
+ stringValue = stringValue.Replace("\"", string.Empty);
+ if (string.IsNullOrWhiteSpace(stringValue))
{
issues.Warning(ValidationErrorCode.RequiredYamlHeaderMissing, $"Missing value for YAML header: {header}");
}
@@ -732,7 +729,7 @@ private void CheckDocumentHeaders(List