NewCodable macros#1808
Conversation
This comment was marked as off-topic.
This comment was marked as off-topic.
d6b408a to
5d52714
Compare
|
Maybe |
I support that. I haven't checked whether it is or not, but we also need to make sure that this is compatible with |
|
Good point, updated it to |
|
Could {
"latitude": 37.332,
"longitude": -122.011,
"additionalInfo": {
"elevation": 30.5
}
}@JSONCodable
struct Coordinate {
var latitude: Double
var longitude: Double
@DecodableAlias("additionalInfo.elevation") var elevation: Double
} |
Eventually yes, but I haven’t identified an obvious quick win to tackle it yet. |
e93dd0d to
76e0a9e
Compare
I've brought this up because it's an example from the documentation of having to resort to manually implementing a decoder |
|
Agreed that is should be possible. Are you aware of any prior art for this kind of annotation in other macro-based systems? I don't see one in Serde's suite of macros. It could make the implementation rather tricky, introducing more complex cases. The "easier" way out would be collecting parsed JSONPrimitives and then decoding all these values from them after the fact, but it's generally better to avoid that if possible. One thing to keep in mind is that we should seek maximal compatibility with the |
|
Looks good! The failures are the same ones we started with (that I still need to address). Merging. Thanks again! |
8557f53
into
swiftlang:experimental/new-codable
The closest official thing I have found, but not macro related, is pydantic from pydantic import BaseModel, Field, AliasPath
class Coordinate(BaseModel):
latitude: float
longitude: float
elevation: float = Field(validation_alias=AliasPath("additionalInfo", "elevation"))
json_data = """
{
"latitude": 37.332,
"longitude": -122.011,
"additionalInfo": {
"elevation": 30.5
}
}
"""
coordinate = Coordinate.model_validate_json(json_data)Serde can custom deserialize a field: #[derive(Debug, Deserialize)]
struct Coordinate {
latitude: f64,
longitude: f64,
#[serde(rename = "additionalInfo", deserialize_with = "deserialize_elevation")]
elevation: f64,
}
fn deserialize_elevation<'de, D>(deserializer: D) -> Result<f64, D::Error>
where
D: Deserializer<'de>,
{
let value: Value = Deserialize::deserialize(deserializer)?;
Ok(value["elevation"].as_f64().unwrap())
}
fn main() {
let json_data = r#"
{
"latitude": 37.332,
"longitude": -122.011,
"additionalInfo": {
"elevation": 30.5
}
}
"#;
let coordinate: Coordinate = serde_json::from_str(json_data).unwrap();
}There's also this crate: #[flat_path]
#[derive(Deserialize)]
struct Foo {
title: String,
foo: bool,
#[flat_path("config.bar.name")]
bar_name: String,
} |
* fix build issues * @JSONEncodable macro * @codingkey macro * @JSONDecodable macro * @JSONCodable macro * @CodableDefault macro * @CodableAlias macro * Code review: docs * Code review: include missing field names * Code review: CodingFields enum * Code review: separate CodingKeys conformances * Code review: permissive handling of unknown keys * Code review: rename alias macro * Code review: #expect(throws:) * Code review: use decodeEachField
Added Swift macros for complementing the NewCodable effort
Motivation:
Several of the existing tests had commented-out
@JSONCodableannotations sketching out what the macro-driven API should eventually look like. This PR picks up that thread and implements the macros.Modifications:
@JSONEncodable: generates the CodingFields enum and JSONEncodable conformance@JSONDecodable: generates JSONDecodable conformance with string-based key matching via decodeEachKeyAndValue@JSONCodable: combines both, delegating to the individual macro implementations@CodingKey("custom_name"): overrides the JSON key for a property@CodableDefault(value): provides a fallback when a key is missing during decoding@CodableAlias("alt1", "alt2"): accepts alternative key names during decodingResult:
Structs can now be annotated with
@JSONCodableto get the same optimized encode/decode code without writing it by hand.Testing:
@CodingKey,@CodableDefault,@CodableAlias, error diagnostics, and the combined@JSONCodablemacro.