Skip to content

Commit ebc50e5

Browse files
authored
Merge pull request #328 from microsoft/jsonattrs
Add AccessPointAttributes JSON serialization/deserialization support
2 parents e85cf6d + 4297f78 commit ebc50e5

File tree

9 files changed

+234
-11
lines changed

9 files changed

+234
-11
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
#include <istream>
3+
#include <optional>
4+
#include <string>
5+
#include <unordered_map>
6+
7+
#include <microsoft/net/wifi/AccessPointAttributes.hxx>
8+
9+
#include "AccessPointAttributesJsonSerialization.hxx"
10+
11+
using namespace Microsoft::Net::Wifi;
12+
13+
/* static */
14+
std::optional<std::unordered_map<std::string, AccessPointAttributes>>
15+
AccessPointAttributes::TryParseJson(const std::string& json)
16+
{
17+
return ParseAccessPointAttributesFromJson(json);
18+
}
19+
20+
/* static */
21+
std::optional<std::unordered_map<std::string, AccessPointAttributes>>
22+
AccessPointAttributes::TryParseJson(std::istream& json)
23+
{
24+
return ParseAccessPointAttributesFromJson(json);
25+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
#include <microsoft/net/wifi/AccessPointAttributes.hxx>
3+
#include <nlohmann/json.hpp>
4+
5+
#include "AccessPointAttributesJsonSerialization.hxx"
6+
7+
namespace Microsoft::Net::Wifi
8+
{
9+
void
10+
to_json(nlohmann::json& accessPointAttributesJson, const AccessPointAttributes& accessPointAttributes)
11+
{
12+
accessPointAttributesJson = nlohmann::json{
13+
{ "Properties", accessPointAttributes.Properties }
14+
};
15+
}
16+
17+
void
18+
from_json(const nlohmann::json& accessPointAttributesJson, AccessPointAttributes& accessPointAttributes)
19+
{
20+
accessPointAttributesJson.at("Properties").get_to(accessPointAttributes.Properties);
21+
}
22+
23+
} // namespace Microsoft::Net::Wifi
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
#ifndef ACCESS_POINT_ATTRIBUTES_JSON_SERIALIZATION_HXX
3+
#define ACCESS_POINT_ATTRIBUTES_JSON_SERIALIZATION_HXX
4+
5+
#include <format>
6+
#include <optional>
7+
#include <string>
8+
#include <unordered_map>
9+
10+
#include <microsoft/net/wifi/AccessPointAttributes.hxx>
11+
#include <nlohmann/json.hpp>
12+
#include <plog/Log.h>
13+
14+
namespace Microsoft::Net::Wifi
15+
{
16+
/**
17+
* @brief Serialize access point attributes to JSON.
18+
*
19+
* @param accessPointAttributesJson The JSON object to hold the serialized data.
20+
* @param accessPointAttributes The access point attributes to serialize.
21+
*/
22+
void
23+
to_json(nlohmann::json& accessPointAttributesJson, const AccessPointAttributes& accessPointAttributes);
24+
25+
/**
26+
* @brief Deserialize access point attributes from JSON.
27+
*
28+
* @param accessPointAttributesJson The JSON object to deserialize.
29+
* @param accessPointAttributes The access point attributes to populate.
30+
*/
31+
void
32+
from_json(const nlohmann::json& accessPointAttributesJson, AccessPointAttributes& accessPointAttributes);
33+
34+
/**
35+
* @brief Parse access point attributes from a JSON input type.
36+
*
37+
* @tparam InputType The input type holding the JSON to parse. This must be one of the types supported by
38+
* nlohmann::json::parse (input stream, string, iterator pair, contiguous container, character array).
39+
* @param json The JSON input to parse.
40+
* @return std::optional<std::unordered_map<std::string, AccessPointAttributes>>
41+
*/
42+
template <typename InputType>
43+
std::optional<std::unordered_map<std::string, AccessPointAttributes>>
44+
ParseAccessPointAttributesFromJson(InputType& json)
45+
{
46+
std::unordered_map<std::string, AccessPointAttributes> accessPointAttributesMap;
47+
nlohmann::json accessPointAttributesJson;
48+
49+
try {
50+
accessPointAttributesJson = nlohmann::json::parse(json);
51+
accessPointAttributesJson.get_to(accessPointAttributesMap);
52+
} catch (const nlohmann::json::parse_error& e) {
53+
LOGE << std::format("Failed to parse access point access point attributes JSON: {}", e.what());
54+
return std::nullopt;
55+
}
56+
57+
return accessPointAttributesMap;
58+
}
59+
60+
} // namespace Microsoft::Net::Wifi
61+
62+
#endif // ACCESS_POINT_ATTRIBUTES_JSON_SERIALIZATION_HXX

src/common/net/wifi/core/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ set(WIFI_CORE_PUBLIC_INCLUDE_PREFIX ${WIFI_CORE_PUBLIC_INCLUDE}/${WIFI_CORE_PUBL
88
target_sources(wifi-core
99
PRIVATE
1010
AccessPoint.cxx
11+
AccessPointAttributes.cxx
12+
AccessPointAttributesJsonSerialization.cxx
13+
AccessPointAttributesJsonSerialization.hxx
1114
AccessPointController.cxx
1215
AccessPointOperationStatus.cxx
1316
AccessPointOperationStatusLogOnExit.cxx
@@ -19,6 +22,7 @@ target_sources(wifi-core
1922
BASE_DIRS ${WIFI_CORE_PUBLIC_INCLUDE}
2023
FILES
2124
${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPoint.hxx
25+
${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPointAttributes.hxx
2226
${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPointController.hxx
2327
${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPointOperationStatus.hxx
2428
${WIFI_CORE_PUBLIC_INCLUDE_PREFIX}/AccessPointOperationStatusLogOnExit.hxx
@@ -33,6 +37,7 @@ target_sources(wifi-core
3337
target_link_libraries(wifi-core
3438
PRIVATE
3539
magic_enum::magic_enum
40+
nlohmann_json::nlohmann_json
3641
notstd
3742
strings
3843
PUBLIC
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
#ifndef ACCESS_POINT_ATTRIBUTES_HXX
3+
#define ACCESS_POINT_ATTRIBUTES_HXX
4+
5+
#include <istream>
6+
#include <optional>
7+
#include <string>
8+
#include <unordered_map>
9+
10+
namespace Microsoft::Net::Wifi
11+
{
12+
/**
13+
* @brief Container to hold static attributes about an access point.
14+
*/
15+
struct AccessPointAttributes
16+
{
17+
/**
18+
* @brief Attempt to deserialize a JSON string into a map of access point attributes.
19+
*
20+
* @param json The JSON input string to parse.
21+
* @return std::optional<std::unordered_map<std::string, AccessPointAttributes>>
22+
*/
23+
static std::optional<std::unordered_map<std::string, AccessPointAttributes>>
24+
TryParseJson(const std::string& json);
25+
26+
/**
27+
* @brief Attempt to deserialize a JSON stream into a map of access point attributes.
28+
*
29+
* @param json The JSON input stream to parse.
30+
* @return std::optional<std::unordered_map<std::string, AccessPointAttributes>>
31+
*/
32+
static std::optional<std::unordered_map<std::string, AccessPointAttributes>>
33+
TryParseJson(std::istream& json);
34+
35+
std::unordered_map<std::string, std::string> Properties{};
36+
};
37+
} // namespace Microsoft::Net::Wifi
38+
39+
#endif // ACCESS_POINT_ATTRIBUTES_HXX

src/common/net/wifi/core/include/microsoft/net/wifi/IAccessPoint.hxx

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,12 @@
66
#include <string_view>
77
#include <unordered_map>
88

9+
#include <microsoft/net/wifi/AccessPointAttributes.hxx>
910
#include <microsoft/net/wifi/IAccessPointController.hxx>
1011
#include <microsoft/net/wifi/Ieee80211.hxx>
1112

1213
namespace Microsoft::Net::Wifi
1314
{
14-
15-
/**
16-
* @brief Container to hold static attributes about an access point.
17-
*/
18-
struct AccessPointAttributes
19-
{
20-
std::unordered_map<std::string, std::string> Properties{};
21-
};
22-
2315
/**
2416
* @brief Represents a wireless access point.
2517
*/

src/linux/net/wifi/apmanager/AccessPointDiscoveryAgentOperationsNetlink.cxx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,8 @@ MakeAccessPoint(const std::shared_ptr<AccessPointFactoryLinux> &accessPointFacto
158158
std::future<std::vector<std::shared_ptr<IAccessPoint>>>
159159
AccessPointDiscoveryAgentOperationsNetlink::ProbeAsync()
160160
{
161-
const auto ToAccessPoint = [this](const Nl80211Interface &nl80211Interface) {
162-
return detail::MakeAccessPoint(m_accessPointFactory, nl80211Interface);
161+
const auto ToAccessPoint = [accessPointFactory = m_accessPointFactory](const Nl80211Interface &nl80211Interface) {
162+
return detail::MakeAccessPoint(accessPointFactory, nl80211Interface);
163163
};
164164

165165
std::promise<std::vector<std::shared_ptr<IAccessPoint>>> probePromise{};

tests/unit/net/wifi/core/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ target_sources(wifi-core-test-unit
55
PRIVATE
66
Main.cxx
77
TestAccessPoint.cxx
8+
TestAccessPointAttributes.cxx
89
TestAccessPointOperationStatus.cxx
910
TestIeee80211.cxx
1011
)
@@ -18,6 +19,7 @@ target_link_libraries(wifi-core-test-unit
1819
PRIVATE
1920
Catch2::Catch2
2021
magic_enum::magic_enum
22+
nlohmann_json::nlohmann_json
2123
plog::plog
2224
strings
2325
wifi-core
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
2+
#include <string>
3+
#include <unordered_map>
4+
5+
#include <catch2/catch_test_macros.hpp>
6+
#include <microsoft/net/wifi/AccessPointAttributes.hxx>
7+
#include <nlohmann/json.hpp>
8+
9+
TEST_CASE("AccessPointAttributes JSON Serialization and Deserialization", "[wifi][core][ap][serialization]")
10+
{
11+
using namespace Microsoft::Net::Wifi;
12+
13+
auto accessPointAttributesJson = R"(
14+
{
15+
"wlan0": {
16+
"Properties": {
17+
"key1": "value1",
18+
"key2": "value2"
19+
}
20+
},
21+
"wlan1": {
22+
"Properties": {
23+
"key1": "value1",
24+
"key2": "value2"
25+
}
26+
}
27+
}
28+
)";
29+
30+
// clang-format off
31+
std::unordered_map<std::string, AccessPointAttributes> AccessPointAttributesMap{
32+
{
33+
"wlan0",
34+
AccessPointAttributes{
35+
{
36+
{ "key1", "value1" },
37+
{ "key2", "value2" }
38+
}
39+
},
40+
},
41+
{
42+
"wlan1",
43+
AccessPointAttributes{
44+
{
45+
{ "key1", "value1" },
46+
{ "key2", "value2" }
47+
}
48+
}
49+
}
50+
};
51+
// clang-format on
52+
53+
SECTION("Deserialization (direct) doesn't cause a crash")
54+
{
55+
REQUIRE_NOTHROW(nlohmann::json::parse(accessPointAttributesJson));
56+
}
57+
58+
SECTION("Deserialization (wrapped) doesn't cause a crash")
59+
{
60+
REQUIRE_NOTHROW(AccessPointAttributes::TryParseJson(accessPointAttributesJson));
61+
}
62+
63+
SECTION("Deserialization (wrapped) populates the access point attributes")
64+
{
65+
auto deserializedAccessPointAttributesOpt = AccessPointAttributes::TryParseJson(accessPointAttributesJson);
66+
REQUIRE(deserializedAccessPointAttributesOpt.has_value());
67+
auto& deserializedAccessPointAttributes = deserializedAccessPointAttributesOpt.value();
68+
REQUIRE(std::size(deserializedAccessPointAttributes) == std::size(AccessPointAttributesMap));
69+
70+
for (const auto& [interfaceName, accessPointAttributes] : AccessPointAttributesMap) {
71+
REQUIRE(deserializedAccessPointAttributes.contains(interfaceName));
72+
REQUIRE(deserializedAccessPointAttributes[interfaceName].Properties == accessPointAttributes.Properties);
73+
}
74+
}
75+
}

0 commit comments

Comments
 (0)