Skip to content

Commit f078b21

Browse files
committed
Escape quotes and backslashes for JSON
1 parent 93e638e commit f078b21

File tree

8 files changed

+60
-70
lines changed

8 files changed

+60
-70
lines changed

src/vswhere.lib/JsonFormatter.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,25 @@
77

88
using namespace std;
99

10+
wstring JsonFormatter::Escape(_In_ const wstring& value)
11+
{
12+
wstring buffer;
13+
wstring::size_type pos = 0;
14+
wstring::size_type last = 0;
15+
16+
while ((pos = value.find_first_of(L"\"\\", last)) != wstring::npos)
17+
{
18+
buffer.append(value, last, pos - last);
19+
buffer.push_back(L'\\');
20+
buffer.push_back(value[pos]);
21+
22+
last = ++pos;
23+
}
24+
25+
buffer += value.substr(last);
26+
return buffer;
27+
}
28+
1029
void JsonFormatter::StartArray(_In_ Console& console, _In_opt_ const std::wstring& name)
1130
{
1231
StartScope(console, JsonScope::Type::array, name);
@@ -21,7 +40,7 @@ void JsonFormatter::WriteProperty(_In_ Console& console, _In_ const wstring& nam
2140
{
2241
StartProperty(console, name);
2342

24-
auto escaped = replace_all(value, L"\\", L"\\\\");
43+
auto escaped = Escape(value);
2544
console.Write(L"\"%ls\"",escaped.c_str());
2645
}
2746

src/vswhere.lib/JsonFormatter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class JsonFormatter :
2626
{
2727
}
2828

29+
static std::wstring Escape(_In_ const std::wstring& value);
30+
2931
bool ShowLogo() const override
3032
{
3133
return false;

src/vswhere.lib/Utilities.h

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,3 @@ struct ci_less : public std::binary_function<std::wstring, std::wstring, bool>
2424
return CSTR_EQUAL > ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lhs.c_str(), lhs.size(), rhs.c_str(), rhs.size());
2525
}
2626
};
27-
28-
template <class _Elem, class _Traits = std::char_traits<_Elem>>
29-
static inline std::basic_string<_Elem, _Traits> replace_all(const std::basic_string<_Elem, _Traits>& source, const _Elem* from, const _Elem* to)
30-
{
31-
std::basic_string<_Elem, _Traits> buffer;
32-
std::basic_string<_Elem, _Traits> _from(from);
33-
34-
std::basic_string<_Elem, _Traits>::size_type pos = 0;
35-
std::basic_string<_Elem, _Traits>::size_type last = 0;
36-
37-
while ((pos = source.find(from, last)) != std::basic_string<_Elem, _Traits>::npos)
38-
{
39-
buffer.append(source, last, pos - last);
40-
buffer += to;
41-
42-
last = pos + _from.length();
43-
}
44-
45-
buffer += source.substr(last);
46-
return buffer;
47-
}

test/vswhere.test/JsonFormatterTests.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ TEST_CLASS(JsonFormatterTests)
1919
{
2020
{ L"InstanceId", L"a1b2c3" },
2121
{ L"InstallationName", L"test" },
22-
{ L"InstallDate", L"2017-02-23T01:22:35Z"}
22+
{ L"InstallDate", L"2017-02-23T01:22:35Z"},
23+
{ L"Description", L"This description contains \"quotes\"." },
2324
};
2425

2526
JsonFormatter sut;
@@ -30,7 +31,8 @@ TEST_CLASS(JsonFormatterTests)
3031
L" {\n"
3132
L" \"instanceId\": \"a1b2c3\",\n"
3233
L" \"installDate\": \"2017-02-23T01:22:35Z\",\n"
33-
L" \"installationName\": \"test\"\n"
34+
L" \"installationName\": \"test\",\n"
35+
L" \"description\": \"This description contains \\\"quotes\\\".\"\n"
3436
L" }\n"
3537
L"]\n";
3638

@@ -506,4 +508,27 @@ TEST_CLASS(JsonFormatterTests)
506508

507509
Assert::AreEqual(expected, console);
508510
}
511+
512+
TEST_METHOD(Escape)
513+
{
514+
vector<tuple<wstring, wstring>> data =
515+
{
516+
{ L"value", L"value" },
517+
{ L"C:\\ShouldNotExist", L"C:\\\\ShouldNotExist" },
518+
{ L"C:\\ShouldNotExist\\Sub", L"C:\\\\ShouldNotExist\\\\Sub" },
519+
{ L"\\\\ShouldNotExist", L"\\\\\\\\ShouldNotExist" },
520+
{ L"\"value\"", L"\\\"value\\\"" },
521+
{ L"\"C:\\ShouldNotExist\"", L"\\\"C:\\\\ShouldNotExist\\\"" },
522+
};
523+
524+
for (const auto& item : data)
525+
{
526+
wstring source, expected;
527+
528+
tie(source, expected) = item;
529+
auto actual = JsonFormatter::Escape(source);
530+
531+
Assert::AreEqual(expected.c_str(), actual.c_str());
532+
}
533+
}
509534
};

test/vswhere.test/TextFormatterTests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ TEST_CLASS(TextFormatterTests)
1919
{
2020
{ L"InstanceId", L"a1b2c3" },
2121
{ L"InstallationName", L"test" },
22+
{ L"Description", L"This description contains \"quotes\"." },
2223
};
2324

2425
TextFormatter sut;
2526
sut.Write(args, console, &instance);
2627

2728
auto expected =
2829
L"instanceId: a1b2c3\n"
29-
L"installationName: test\n";
30+
L"installationName: test\n"
31+
L"description: This description contains \"quotes\".\n";
3032

3133
Assert::AreEqual(expected, console);
3234
}

test/vswhere.test/UtilitiesTests.cpp

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -60,46 +60,4 @@ TEST_CLASS(UtilitiesTests)
6060
Assert::AreEqual(expected, actual, format(L"ci_less(%ls, %ls)", lhs.c_str(), rhs.c_str()).c_str());
6161
}
6262
}
63-
64-
TEST_METHOD(replace_all_theory_string)
65-
{
66-
vector<tuple<string, string>> data =
67-
{
68-
{ "value", "value" },
69-
{ "C:\\ShouldNotExist", "C:\\\\ShouldNotExist" },
70-
{ "C:\\ShouldNotExist\\Sub", "C:\\\\ShouldNotExist\\\\Sub" },
71-
{ "\\\\ShouldNotExist", "\\\\\\\\ShouldNotExist" },
72-
};
73-
74-
for (const auto& item : data)
75-
{
76-
string source, expected;
77-
78-
tie(source, expected) = item;
79-
auto actual = replace_all(source, "\\", "\\\\");
80-
81-
Assert::AreEqual(expected.c_str(), actual.c_str());
82-
}
83-
}
84-
85-
TEST_METHOD(replace_all_theory_wstring)
86-
{
87-
vector<tuple<wstring, wstring>> data =
88-
{
89-
{ L"value", L"value" },
90-
{ L"C:\\ShouldNotExist", L"C:\\\\ShouldNotExist" },
91-
{ L"C:\\ShouldNotExist\\Sub", L"C:\\\\ShouldNotExist\\\\Sub" },
92-
{ L"\\\\ShouldNotExist", L"\\\\\\\\ShouldNotExist" },
93-
};
94-
95-
for (const auto& item : data)
96-
{
97-
wstring source, expected;
98-
99-
tie(source, expected) = item;
100-
auto actual = replace_all(source, L"\\", L"\\\\");
101-
102-
Assert::AreEqual(expected.c_str(), actual.c_str());
103-
}
104-
}
10563
};

test/vswhere.test/ValueFormatterTests.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,22 @@ TEST_CLASS(ValueFormatterTests)
1414
TEST_METHOD(Write_Instance)
1515
{
1616
CommandArgs args;
17-
args.Parse(L"vswhere.exe -property instanceId");
17+
args.Parse(L"vswhere.exe");
1818

1919
TestConsole console(args);
2020
TestInstance instance =
2121
{
2222
{ L"InstanceId", L"a1b2c3" },
2323
{ L"InstallationName", L"test" },
24+
{ L"Description", L"This description contains \"quotes\"." },
2425
};
2526

2627
ValueFormatter sut;
2728
sut.Write(args, console, &instance);
2829

29-
auto expected = L"a1b2c3\n";
30+
auto expected = L"a1b2c3\n"
31+
L"test\n"
32+
L"This description contains \"quotes\".\n";
3033

3134
Assert::AreEqual(expected, console);
3235
}

test/vswhere.test/XmlFormatterTests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ TEST_CLASS(XmlFormatterTests)
1919
{
2020
{ L"InstanceId", L"a1b2c3" },
2121
{ L"InstallationName", L"test" },
22-
{ L"InstallDate", L"2017-02-23T01:22:35Z" }
22+
{ L"InstallDate", L"2017-02-23T01:22:35Z" },
23+
{ L"Description", L"This description contains \"quotes\"." },
2324
};
2425

2526
XmlFormatter sut;
@@ -32,6 +33,7 @@ TEST_CLASS(XmlFormatterTests)
3233
L" <instanceId>a1b2c3</instanceId>\n"
3334
L" <installDate>2017-02-23T01:22:35Z</installDate>\n"
3435
L" <installationName>test</installationName>\n"
36+
L" <description>This description contains \"quotes\".</description>\n"
3537
L" </instance>\n"
3638
L"</instances>\n";
3739

0 commit comments

Comments
 (0)