Skip to content

Commit 58434a8

Browse files
committed
Add literal style support for OpenApiYamlWriter
1 parent 3d62054 commit 58434a8

File tree

3 files changed

+129
-4
lines changed

3 files changed

+129
-4
lines changed

src/Microsoft.OpenApi/Writers/OpenApiWriterBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public abstract class OpenApiWriterBase : IOpenApiWriter
2424
/// <summary>
2525
/// The indentation string to prepand to each line for each indentation level.
2626
/// </summary>
27-
private const string IndentationString = " ";
27+
protected const string IndentationString = " ";
2828

2929
/// <summary>
3030
/// Scope of the Open API element - object, array, property.

src/Microsoft.OpenApi/Writers/OpenApiYamlWriter.cs

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public OpenApiYamlWriter(TextWriter textWriter, OpenApiWriterSettings settings =
2020

2121
}
2222

23+
public bool UseLiteralStyle { get; set; }
24+
2325
/// <summary>
2426
/// Base Indentation Level.
2527
/// This denotes how many indentations are needed for the property in the base object.
@@ -156,11 +158,76 @@ public override void WritePropertyName(string name)
156158
/// <param name="value">The string value.</param>
157159
public override void WriteValue(string value)
158160
{
159-
WriteValueSeparator();
161+
if (!UseLiteralStyle || value.IndexOfAny(new [] { '\n', '\r' }) == -1)
162+
{
163+
WriteValueSeparator();
160164

161-
value = value.GetYamlCompatibleString();
165+
value = value.GetYamlCompatibleString();
162166

163-
Writer.Write(value);
167+
Writer.Write(value);
168+
}
169+
else
170+
{
171+
if (CurrentScope() != null)
172+
{
173+
WriteValueSeparator();
174+
}
175+
176+
Writer.Write("|");
177+
178+
// Write chomping indicator when it ends with line break.
179+
if (value.LastIndexOfAny(new []{'\n', '\r'}) == value.Length - 1)
180+
{
181+
Writer.Write("+");
182+
}
183+
184+
// Write indentation indicator when it starts with spaces
185+
if (value.StartsWith(" "))
186+
{
187+
Writer.Write(IndentationString.Length);
188+
}
189+
190+
Writer.WriteLine();
191+
192+
IncreaseIndentation();
193+
194+
var start = 0;
195+
while (start < value.Length && value.IndexOfAny(new [] {'\n', '\r'}, start) is var lineBreak)
196+
{
197+
if (lineBreak == -1)
198+
{
199+
break;
200+
}
201+
202+
// To preserves line break characters.
203+
var end = lineBreak;
204+
if (lineBreak + 1 < value.Length && value[lineBreak] == '\r' && value[lineBreak + 1] == '\n')
205+
{
206+
// The line ends with "\r\n". YAML 1.2 specifies only three line breaks. "\r\n" | "\r" | "\n"
207+
end++;
208+
}
209+
210+
if (lineBreak - start != 0)
211+
{
212+
// Indentation for empty lines aren't needed.
213+
WriteIndentation();
214+
}
215+
216+
var line = value.Substring(start, end - start + 1);
217+
Writer.Write(line);
218+
219+
start = end + 1;
220+
}
221+
222+
// Last line
223+
if (start < value.Length)
224+
{
225+
WriteIndentation();
226+
Writer.Write(value.Substring(start));
227+
}
228+
229+
DecreaseIndentation();
230+
}
164231
}
165232

166233
/// <summary>

test/Microsoft.OpenApi.Tests/Writers/OpenApiWriterSpecialCharacterTests.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,63 @@ public void WriteStringWithSpecialCharactersAsYamlWorks(string input, string exp
7575
// Assert
7676
actual.Should().Be(expected);
7777
}
78+
79+
[Theory]
80+
[InlineData("multiline\r\nstring", "test: |\n multiline\r\n string")]
81+
[InlineData("multiline\nstring", "test: |\n multiline\n string")]
82+
[InlineData("multiline\rstring", "test: |\n multiline\r string")]
83+
[InlineData("multiline\n\rstring", "test: |\n multiline\n\r string")]
84+
[InlineData("ends with\r\nline break\r\n", "test: |+\n ends with\r\n line break\r\n")]
85+
[InlineData("ends with\nline break\n", "test: |+\n ends with\n line break\n")]
86+
[InlineData("ends with\rline break\r", "test: |+\n ends with\r line break\r")]
87+
[InlineData("ends with\n\rline break\n\r", "test: |+\n ends with\n\r line break\n\r")]
88+
[InlineData(" starts with\nspaces", "test: |2\n starts with\n spaces")]
89+
[InlineData(" starts with\nspaces, and ends with line break\n", "test: |+2\n starts with\n spaces, and ends with line break\n")]
90+
[InlineData("contains\n\n\nempty lines", "test: |\n contains\n\n\n empty lines")]
91+
[InlineData("no line breaks fallback ", "test: 'no line breaks fallback '")]
92+
public void WriteStringWithNewlineCharactersInObjectAsYamlWorks(string input, string expected)
93+
{
94+
// Arrange
95+
var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
96+
var writer = new OpenApiYamlWriter(outputStringWriter) { UseLiteralStyle = true, };
97+
98+
// Act
99+
writer.WriteStartObject();
100+
writer.WritePropertyName("test");
101+
writer.WriteValue(input);
102+
writer.WriteEndObject();
103+
var actual = outputStringWriter.GetStringBuilder().ToString();
104+
105+
// Assert
106+
actual.Should().Be(expected);
107+
}
108+
109+
[Theory]
110+
[InlineData("multiline\r\nstring", "- |\n multiline\r\n string")]
111+
[InlineData("multiline\nstring", "- |\n multiline\n string")]
112+
[InlineData("multiline\rstring", "- |\n multiline\r string")]
113+
[InlineData("multiline\n\rstring", "- |\n multiline\n\r string")]
114+
[InlineData("ends with\r\nline break\r\n", "- |+\n ends with\r\n line break\r\n")]
115+
[InlineData("ends with\nline break\n", "- |+\n ends with\n line break\n")]
116+
[InlineData("ends with\rline break\r", "- |+\n ends with\r line break\r")]
117+
[InlineData("ends with\n\rline break\n\r", "- |+\n ends with\n\r line break\n\r")]
118+
[InlineData(" starts with\nspaces", "- |2\n starts with\n spaces")]
119+
[InlineData(" starts with\nspaces, and ends with line break\n", "- |+2\n starts with\n spaces, and ends with line break\n")]
120+
[InlineData("contains\n\n\nempty lines", "- |\n contains\n\n\n empty lines")]
121+
[InlineData("no line breaks fallback ", "- 'no line breaks fallback '")]
122+
public void WriteStringWithNewlineCharactersInArrayAsYamlWorks(string input, string expected)
123+
{
124+
// Arrange
125+
var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture);
126+
var writer = new OpenApiYamlWriter(outputStringWriter) { UseLiteralStyle = true, };
127+
128+
// Act
129+
writer.WriteStartArray();
130+
writer.WriteValue(input);
131+
var actual = outputStringWriter.GetStringBuilder().ToString();
132+
133+
// Assert
134+
actual.Should().Be(expected);
135+
}
78136
}
79137
}

0 commit comments

Comments
 (0)