Skip to content

Commit e39804b

Browse files
authored
fix: allow deserialization of optional decimals (#139)
the 'rust_decimal::serde::arbitrary_precision_option' macro requires 'default' to be specified to allow deserializing the objects when the value is not present
1 parent f655062 commit e39804b

File tree

5 files changed

+133
-11
lines changed

5 files changed

+133
-11
lines changed

src/tests/schema_validation/v2_0_1.rs

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2226,6 +2226,113 @@ mod tests {
22262226
}
22272227
assert!(compiled.is_valid(&instance));
22282228
}
2229+
/**
2230+
* Some optional fields including maxLimit of the VariableCharacteristicsType are not
2231+
* included in the payload in order to validate deserialization of optional fields.
2232+
*/
2233+
#[test]
2234+
fn validate_notify_report_request_from_json() {
2235+
let json = r#"{
2236+
"generatedAt": "2024-12-23T03:38:31.625Z",
2237+
"reportData": [
2238+
{
2239+
"component": {
2240+
"name": "AlignedDataCtrlr"
2241+
},
2242+
"variable": {
2243+
"name": "Interval"
2244+
},
2245+
"variableAttribute": [
2246+
{
2247+
"mutability": "ReadWrite",
2248+
"value": "10"
2249+
}
2250+
],
2251+
"variableCharacteristics": {
2252+
"dataType": "integer",
2253+
"supportsMonitoring": true,
2254+
"unit": "seconds"
2255+
}
2256+
},
2257+
{
2258+
"component": {
2259+
"name": "AlignedDataCtrlr"
2260+
},
2261+
"variable": {
2262+
"name": "Measurands"
2263+
},
2264+
"variableAttribute": [
2265+
{
2266+
"mutability": "ReadWrite",
2267+
"value": "Energy.Active.Import.Register"
2268+
}
2269+
],
2270+
"variableCharacteristics": {
2271+
"dataType": "MemberList",
2272+
"supportsMonitoring": true
2273+
}
2274+
},
2275+
{
2276+
"component": {
2277+
"name": "AlignedDataCtrlr"
2278+
},
2279+
"variable": {
2280+
"name": "TxEndedInterval"
2281+
},
2282+
"variableAttribute": [
2283+
{
2284+
"mutability": "ReadWrite",
2285+
"value": "10"
2286+
}
2287+
],
2288+
"variableCharacteristics": {
2289+
"dataType": "integer",
2290+
"supportsMonitoring": true,
2291+
"unit": "seconds"
2292+
}
2293+
},
2294+
{
2295+
"component": {
2296+
"name": "AlignedDataCtrlr"
2297+
},
2298+
"variable": {
2299+
"name": "TxEndedMeasurands"
2300+
},
2301+
"variableAttribute": [
2302+
{
2303+
"mutability": "ReadWrite",
2304+
"value": "Energy.Active.Import.Register"
2305+
}
2306+
],
2307+
"variableCharacteristics": {
2308+
"dataType": "MemberList",
2309+
"supportsMonitoring": true,
2310+
"minLimit": 0
2311+
}
2312+
}
2313+
],
2314+
"requestId": 1,
2315+
"seqNo": 0,
2316+
"tbc": true
2317+
}"#;
2318+
2319+
// verify that the JSON can be deserialized into a NotifyReportRequest object
2320+
let request: NotifyReportRequest = serde_json::from_str(json).unwrap();
2321+
assert_eq!(request.request_id, 1);
2322+
2323+
let schema = include_str!("schemas/v2.0.1/NotifyReportRequest.json");
2324+
let schema = serde_json::from_str(schema).unwrap();
2325+
let instance = serde_json::from_str(json).unwrap();
2326+
let compiled = Validator::new(&schema).expect("A valid schema");
2327+
let result = compiled.validate(&instance);
2328+
if let Err(errors) = result {
2329+
for error in errors {
2330+
println!("Validation error: {}", error);
2331+
println!("Instance path: {}", error.instance_path);
2332+
}
2333+
}
2334+
assert!(compiled.is_valid(&instance));
2335+
}
22292336
#[test]
22302337
fn validate_notify_report_request() {
22312338
let test = NotifyReportRequest {
@@ -2257,7 +2364,7 @@ mod tests {
22572364
unit: Some("unit".to_string()),
22582365
data_type: DataEnumType::String,
22592366
min_limit: Some(dec!(0.0)),
2260-
max_limit: Some(dec!(0.0)),
2367+
max_limit: None,
22612368
values_list: Some("values_list".to_string()),
22622369
supports_monitoring: false,
22632370
}),

src/v1_6/types/charging_schedule.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ pub struct ChargingSchedule {
1818
/// Required. List of ChargingSchedulePeriod elements defining maximum power or current usage over time. The startSchedule of the first ChargingSchedulePeriod SHALL always be 0.
1919
pub charging_schedule_period: Vec<ChargingSchedulePeriod>,
2020
/// Optional. Minimum charging rate supported by the electric vehicle. The unit of measure is defined by the chargingRateUnit. This parameter is intended to be used by a local smart charging algorithm to optimize the power allocation for in the case a charging process is inefficient at lower charging rates. Accepts at most one digit fraction (e.g. 8.1)
21-
#[serde(skip_serializing_if = "Option::is_none")]
22-
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
21+
#[serde(
22+
with = "rust_decimal::serde::arbitrary_precision_option",
23+
skip_serializing_if = "Option::is_none",
24+
default
25+
)]
2326
pub min_charging_rate: Option<Decimal>,
2427
}

src/v2_0_1/datatypes/charging_schedule_type.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,11 @@ pub struct ChargingScheduleType {
2323
/// Required. The unit of measure Limit is expressed in.
2424
pub charging_rate_unit: ChargingRateUnitEnumType,
2525
/// Optional. Minimum charging rate supported by the EV. The unit of measure is defined by the chargingRateUnit. This parameter is intended to be used by a local smart charging algorithm to optimize the power allocation for in the case a charging process is inefficient at lower charging rates. Accepts at most one digit fraction (e.g. 8.1)
26-
#[serde(skip_serializing_if = "Option::is_none")]
27-
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
26+
#[serde(
27+
with = "rust_decimal::serde::arbitrary_precision_option",
28+
skip_serializing_if = "Option::is_none",
29+
default
30+
)]
2831
pub min_charging_rate: Option<Decimal>,
2932
/// Required. List of ChargingSchedulePeriod elements defining maximum power or current usage over time. The maximum number of periods, that is supported by the Charging Station, if less than 1024, is set by device model variable SmartChargingCtrlr.PeriodsPerSchedule
3033
#[validate(length(min = 1))]

src/v2_0_1/datatypes/variable_characteristics_type.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ pub struct VariableCharacteristicsType {
1010
#[serde(skip_serializing_if = "Option::is_none")]
1111
pub unit: Option<String>,
1212
pub data_type: DataEnumType,
13-
#[serde(skip_serializing_if = "Option::is_none")]
14-
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
13+
#[serde(
14+
with = "rust_decimal::serde::arbitrary_precision_option",
15+
skip_serializing_if = "Option::is_none",
16+
default
17+
)]
1518
pub min_limit: Option<Decimal>,
16-
#[serde(skip_serializing_if = "Option::is_none")]
17-
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
19+
#[serde(
20+
with = "rust_decimal::serde::arbitrary_precision_option",
21+
skip_serializing_if = "Option::is_none",
22+
default
23+
)]
1824
pub max_limit: Option<Decimal>,
1925
#[serde(skip_serializing_if = "Option::is_none")]
2026
pub values_list: Option<String>,

src/v2_0_1/messages/transaction_event.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ pub struct TransactionEventRequest {
4040
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Default)]
4141
#[serde(rename_all = "camelCase")]
4242
pub struct TransactionEventResponse {
43-
#[serde(skip_serializing_if = "Option::is_none")]
44-
#[serde(with = "rust_decimal::serde::arbitrary_precision_option")]
43+
#[serde(
44+
with = "rust_decimal::serde::arbitrary_precision_option",
45+
skip_serializing_if = "Option::is_none",
46+
default
47+
)]
4548
pub total_cost: Option<Decimal>,
4649
#[serde(skip_serializing_if = "Option::is_none")]
4750
pub charging_priority: Option<i32>,

0 commit comments

Comments
 (0)