-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Encode element with empty key, no elements, and attributes #224
Comments
Hi @MaxDesiatov In taking a step back, I tried to avoid using empty keys, but unfortunately, I am unable to find a way to achieve this in an elegant/generic manner. To re-cap, in the initial post, we have two data structures: In
In
Talking to you offline, I explored two alternatives which avoids using an empty key -- both of them don't work, but for different reasons. Approach 1: Make
public struct SimpleScalarPropertiesInput: Encodable, DynamicNodeEncoding {
public let nested: NestedWithNamespace?
enum CodingKeys: String, CodingKey {
case xmlns
case nested = "Nested"
}
public static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
let xmlNamespaceValues = [
"xmlns"
]
if let key = key as? CodingKeys {
if xmlNamespaceValues.contains(key.stringValue) {
return .attribute
}
}
return .element
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if encoder.codingPath.isEmpty {
try container.encode("https://example.com", forKey: .xmlns)
}
if let nested = nested {
try container.encode(nested, forKey: .nested)
}
}
} Notice how the This seems fine on the surface, but there is a problem when it comes to public struct NestedWithNamespace: Encodable, DynamicNodeEncoding {
public let attrField: String?
enum CodingKeys: String, CodingKey {
case xsiNamespace = "xmlns:xsi"
case someName = "xsi:someName"
}
public static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
let codingKeys = [
"xmlns:xsi",
"xsi:someName"
]
if let key = key as? CodingKeys {
if codingKeys.contains(key.stringValue) {
return .attribute
}
}
return .element
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
// [ The following is problematic]
//try container.encode("https://example.com", forKey: .xsiNamespace)
if let attrField = attrField {
try container.encode(attrField, forKey: .someName)
}
}
} Notice the comment in For example: struct SimpleScalarPropertiesInputV2 {
public let nested: NestedWithNamespace?
...snip...
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
... snip ...
// Using NestedWithNamespace's `encode()` function won't work, because the namespace is applied to
// the `nested` member of this type (which can be different than the namespace defined against
// the other type: SimpleScalarPropertiesInput)
if let nested = nested {
try container.encode(nested, forKey: .nested)
}
}
} Approach 2: Make In this approach, public struct SimpleScalarPropertiesInput: Encodable, DynamicNodeEncoding {
public let nested: NestedWithNamespace?
enum CodingKeys: String, CodingKey {
case xmlns
case nested = "Nested"
case xmlns_xsi = "xmlns:xsi"
case xsi_someName = "xsi:someName"
}
public static func nodeEncoding(for key: CodingKey) -> XMLEncoder.NodeEncoding {
let xmlNamespaceValues = [
"xmlns",
"xmlns:xsi",
"xsi:someName"
]
if let key = key as? CodingKeys {
if xmlNamespaceValues.contains(key.stringValue) {
return .attribute
}
}
return .element
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if encoder.codingPath.isEmpty {
try container.encode("https://example.com", forKey: .xmlns)
}
if let nested = nested {
var nestedContainer = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .nested)
try nestedContainer.encode("https://example.com", forKey: .xmlns_xsi)
if let attrField = nested.attrField {
try nestedContainer.encode(attrField, forKey: .xsi_someName)
}
}
}
} The code snippet above does render the correct xmlstring, however, this breaks encapsulation, because we are making Unfortunately, I don't have any other ideas at the moment, as it doesn't seem possible to pass namespaces/arguments between the |
Thanks again! Resolving: |
Hi @MaxDesiatov
I may have found an issue with the way we are serializing XML.
Consider a top level structure
SimpleScalarPropertiesInput
:Notice that
SimpleScalarPropertiesInput
has a single member callednested
of typeNestedWithNamespace
:Note that we are using
Key
as a way to use custom CodingKey values (rather than havingenum CodingKey: String, CodingKey
).Finally, exercising this code fails the
assert
statementInstead, I'm observing that encodedString is producing invalid XML:
(notice that the attribute/value
xsi:someName="nestedAttrValue"
is not part of the element)It seems that the expected string should be:
I've come up with a preliminary approach to fixing this, but would like to get your take on the approach before I start surrounding this with unit tests.
My preliminary approach is posted here:
#223
Thanks again for all the support!
The text was updated successfully, but these errors were encountered: