Skip to content

Commit

Permalink
Escape quotes and backslashes for JSON
Browse files Browse the repository at this point in the history
  • Loading branch information
heaths committed Jan 23, 2019
1 parent 93e638e commit f078b21
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 70 deletions.
21 changes: 20 additions & 1 deletion src/vswhere.lib/JsonFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,25 @@

using namespace std;

wstring JsonFormatter::Escape(_In_ const wstring& value)
{
wstring buffer;
wstring::size_type pos = 0;
wstring::size_type last = 0;

while ((pos = value.find_first_of(L"\"\\", last)) != wstring::npos)
{
buffer.append(value, last, pos - last);
buffer.push_back(L'\\');
buffer.push_back(value[pos]);

last = ++pos;
}

buffer += value.substr(last);
return buffer;
}

void JsonFormatter::StartArray(_In_ Console& console, _In_opt_ const std::wstring& name)
{
StartScope(console, JsonScope::Type::array, name);
Expand All @@ -21,7 +40,7 @@ void JsonFormatter::WriteProperty(_In_ Console& console, _In_ const wstring& nam
{
StartProperty(console, name);

auto escaped = replace_all(value, L"\\", L"\\\\");
auto escaped = Escape(value);
console.Write(L"\"%ls\"",escaped.c_str());
}

Expand Down
2 changes: 2 additions & 0 deletions src/vswhere.lib/JsonFormatter.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class JsonFormatter :
{
}

static std::wstring Escape(_In_ const std::wstring& value);

bool ShowLogo() const override
{
return false;
Expand Down
21 changes: 0 additions & 21 deletions src/vswhere.lib/Utilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,3 @@ struct ci_less : public std::binary_function<std::wstring, std::wstring, bool>
return CSTR_EQUAL > ::CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, lhs.c_str(), lhs.size(), rhs.c_str(), rhs.size());
}
};

template <class _Elem, class _Traits = std::char_traits<_Elem>>
static inline std::basic_string<_Elem, _Traits> replace_all(const std::basic_string<_Elem, _Traits>& source, const _Elem* from, const _Elem* to)
{
std::basic_string<_Elem, _Traits> buffer;
std::basic_string<_Elem, _Traits> _from(from);

std::basic_string<_Elem, _Traits>::size_type pos = 0;
std::basic_string<_Elem, _Traits>::size_type last = 0;

while ((pos = source.find(from, last)) != std::basic_string<_Elem, _Traits>::npos)
{
buffer.append(source, last, pos - last);
buffer += to;

last = pos + _from.length();
}

buffer += source.substr(last);
return buffer;
}
29 changes: 27 additions & 2 deletions test/vswhere.test/JsonFormatterTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ TEST_CLASS(JsonFormatterTests)
{
{ L"InstanceId", L"a1b2c3" },
{ L"InstallationName", L"test" },
{ L"InstallDate", L"2017-02-23T01:22:35Z"}
{ L"InstallDate", L"2017-02-23T01:22:35Z"},
{ L"Description", L"This description contains \"quotes\"." },
};

JsonFormatter sut;
Expand All @@ -30,7 +31,8 @@ TEST_CLASS(JsonFormatterTests)
L" {\n"
L" \"instanceId\": \"a1b2c3\",\n"
L" \"installDate\": \"2017-02-23T01:22:35Z\",\n"
L" \"installationName\": \"test\"\n"
L" \"installationName\": \"test\",\n"
L" \"description\": \"This description contains \\\"quotes\\\".\"\n"
L" }\n"
L"]\n";

Expand Down Expand Up @@ -506,4 +508,27 @@ TEST_CLASS(JsonFormatterTests)

Assert::AreEqual(expected, console);
}

TEST_METHOD(Escape)
{
vector<tuple<wstring, wstring>> data =
{
{ L"value", L"value" },
{ L"C:\\ShouldNotExist", L"C:\\\\ShouldNotExist" },
{ L"C:\\ShouldNotExist\\Sub", L"C:\\\\ShouldNotExist\\\\Sub" },
{ L"\\\\ShouldNotExist", L"\\\\\\\\ShouldNotExist" },
{ L"\"value\"", L"\\\"value\\\"" },
{ L"\"C:\\ShouldNotExist\"", L"\\\"C:\\\\ShouldNotExist\\\"" },
};

for (const auto& item : data)
{
wstring source, expected;

tie(source, expected) = item;
auto actual = JsonFormatter::Escape(source);

Assert::AreEqual(expected.c_str(), actual.c_str());
}
}
};
4 changes: 3 additions & 1 deletion test/vswhere.test/TextFormatterTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ TEST_CLASS(TextFormatterTests)
{
{ L"InstanceId", L"a1b2c3" },
{ L"InstallationName", L"test" },
{ L"Description", L"This description contains \"quotes\"." },
};

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

auto expected =
L"instanceId: a1b2c3\n"
L"installationName: test\n";
L"installationName: test\n"
L"description: This description contains \"quotes\".\n";

Assert::AreEqual(expected, console);
}
Expand Down
42 changes: 0 additions & 42 deletions test/vswhere.test/UtilitiesTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,46 +60,4 @@ TEST_CLASS(UtilitiesTests)
Assert::AreEqual(expected, actual, format(L"ci_less(%ls, %ls)", lhs.c_str(), rhs.c_str()).c_str());
}
}

TEST_METHOD(replace_all_theory_string)
{
vector<tuple<string, string>> data =
{
{ "value", "value" },
{ "C:\\ShouldNotExist", "C:\\\\ShouldNotExist" },
{ "C:\\ShouldNotExist\\Sub", "C:\\\\ShouldNotExist\\\\Sub" },
{ "\\\\ShouldNotExist", "\\\\\\\\ShouldNotExist" },
};

for (const auto& item : data)
{
string source, expected;

tie(source, expected) = item;
auto actual = replace_all(source, "\\", "\\\\");

Assert::AreEqual(expected.c_str(), actual.c_str());
}
}

TEST_METHOD(replace_all_theory_wstring)
{
vector<tuple<wstring, wstring>> data =
{
{ L"value", L"value" },
{ L"C:\\ShouldNotExist", L"C:\\\\ShouldNotExist" },
{ L"C:\\ShouldNotExist\\Sub", L"C:\\\\ShouldNotExist\\\\Sub" },
{ L"\\\\ShouldNotExist", L"\\\\\\\\ShouldNotExist" },
};

for (const auto& item : data)
{
wstring source, expected;

tie(source, expected) = item;
auto actual = replace_all(source, L"\\", L"\\\\");

Assert::AreEqual(expected.c_str(), actual.c_str());
}
}
};
7 changes: 5 additions & 2 deletions test/vswhere.test/ValueFormatterTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,22 @@ TEST_CLASS(ValueFormatterTests)
TEST_METHOD(Write_Instance)
{
CommandArgs args;
args.Parse(L"vswhere.exe -property instanceId");
args.Parse(L"vswhere.exe");

TestConsole console(args);
TestInstance instance =
{
{ L"InstanceId", L"a1b2c3" },
{ L"InstallationName", L"test" },
{ L"Description", L"This description contains \"quotes\"." },
};

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

auto expected = L"a1b2c3\n";
auto expected = L"a1b2c3\n"
L"test\n"
L"This description contains \"quotes\".\n";

Assert::AreEqual(expected, console);
}
Expand Down
4 changes: 3 additions & 1 deletion test/vswhere.test/XmlFormatterTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ TEST_CLASS(XmlFormatterTests)
{
{ L"InstanceId", L"a1b2c3" },
{ L"InstallationName", L"test" },
{ L"InstallDate", L"2017-02-23T01:22:35Z" }
{ L"InstallDate", L"2017-02-23T01:22:35Z" },
{ L"Description", L"This description contains \"quotes\"." },
};

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

Expand Down

0 comments on commit f078b21

Please sign in to comment.