@@ -187,6 +187,31 @@ open class XMLEncoder {
187187 case expandListWithItemTag( String )
188188 }
189189
190+ /// The strategy to use when encoding maps.
191+ public enum MapEncodingStrategy {
192+ /// Preserves the XML structure. Key and Value tags will be retained producing a
193+ /// list of elements with these two attributes. This is the default strategy.
194+ case preserveStructure
195+
196+ /// Expands the XML structure to avoid key and value tags. For example-
197+ /// <Result>
198+ /// <Tag>
199+ /// <Key>QueueType</Key>
200+ /// <Value>Production</Value>
201+ /// </Tag>
202+ /// <Tag>
203+ /// <Key>Owner</Key>
204+ /// <Value>Developer123</Value>
205+ /// </Tag>
206+ /// </Result>
207+ ///
208+ /// will be encoded from
209+ /// struct Result {
210+ /// let tags: [String: String]
211+ /// }
212+ case expandMapUsingTags( keyTag: String , valueTag: String )
213+ }
214+
190215 /// The output format to produce. Defaults to `[]`.
191216 open var outputFormatting : OutputFormatting = [ ]
192217
@@ -211,6 +236,9 @@ open class XMLEncoder {
211236 /// The strategy to use in encoding lists. Defaults to `.preserveStructure`.
212237 open var listEncodingStrategy : ListEncodingStrategy = . preserveStructure
213238
239+ /// The strategy to use in encoding map. Defaults to `.preserveStructure`.
240+ open var mapEncodingStrategy : MapEncodingStrategy = . preserveStructure
241+
214242 /// Contextual user-provided information for use during encoding.
215243 open var userInfo : [ CodingUserInfoKey : Any ] = [ : ]
216244
@@ -223,6 +251,7 @@ open class XMLEncoder {
223251 let attributeEncodingStrategy : AttributeEncodingStrategy
224252 let stringEncodingStrategy : StringEncodingStrategy
225253 let listEncodingStrategy : ListEncodingStrategy
254+ let mapEncodingStrategy : MapEncodingStrategy
226255 let userInfo : [ CodingUserInfoKey : Any ]
227256 }
228257
@@ -235,6 +264,7 @@ open class XMLEncoder {
235264 attributeEncodingStrategy: attributeEncodingStrategy,
236265 stringEncodingStrategy: stringEncodingStrategy,
237266 listEncodingStrategy: listEncodingStrategy,
267+ mapEncodingStrategy: mapEncodingStrategy,
238268 userInfo: userInfo)
239269 }
240270
@@ -936,6 +966,9 @@ extension _XMLEncoder {
936966 return . uint64( UInt64 ( int) )
937967 } else if let int = value as? UInt32 {
938968 return . uint64( UInt64 ( int) )
969+ } else if let boxableEncodable = value as? BoxableEncodable {
970+ // this type knows how to box itself
971+ return try boxableEncodable. box ( forEncoder: self )
939972 }
940973
941974 let depth = self . storage. count
@@ -950,3 +983,58 @@ extension _XMLEncoder {
950983 }
951984}
952985
986+ /// Protocol for a type that knows how to box itself
987+ protocol BoxableEncodable : Encodable {
988+ func box( forEncoder encoder: _XMLEncoder ) throws -> MutableContainer ?
989+ }
990+
991+ extension Dictionary : BoxableEncodable where Key == String , Value: Encodable {
992+ /// function to box (and potentially expand
993+ internal func box( forEncoder encoder: _XMLEncoder ) throws -> MutableContainer ? {
994+ let depth = encoder. storage. count
995+ // if we are expanding maps
996+ if case let . expandMapUsingTags( keyTag: keyTag, valueTag: valueTag) = encoder. options. mapEncodingStrategy {
997+ let outerMutableContainer : ( MutableContainerDictionary , String ) ?
998+ // if we are expanding lists, wrap everything in a dictionary with that item
999+ if case let . expandListWithItemTag( itemTag) = encoder. options. listEncodingStrategy {
1000+ let newMutableContainerDictionary = MutableContainerDictionary ( )
1001+ encoder. storage. push ( container: . dictionary( newMutableContainerDictionary) )
1002+ outerMutableContainer = ( newMutableContainerDictionary, itemTag)
1003+ } else {
1004+ outerMutableContainer = nil
1005+ }
1006+
1007+ // create the expanded array
1008+ let mutableContainerArray = MutableContainerArray ( )
1009+ encoder. storage. push ( container: . array( mutableContainerArray) )
1010+
1011+ try self . forEach { ( key, value) in
1012+ // create the dictionary for the key and value entries
1013+ let mutableContainerDictionary = MutableContainerDictionary ( )
1014+ encoder. storage. push ( container: . dictionary( mutableContainerDictionary) )
1015+ mutableContainerDictionary [ keyTag] = . string( key)
1016+ mutableContainerDictionary [ valueTag] = try encoder. box ( value)
1017+
1018+ // add to the array
1019+ mutableContainerArray. append ( encoder. storage. popContainer ( ) )
1020+ }
1021+
1022+ // add to the outer container if it is needed
1023+ if let outerMutableContainer = outerMutableContainer {
1024+ outerMutableContainer. 0 [ outerMutableContainer. 1 ] = encoder. storage. popContainer ( )
1025+ }
1026+ } else {
1027+ // otherwise just encode this array as normal
1028+ try self . encode ( to: encoder)
1029+ }
1030+
1031+ // The top container should be a new container.
1032+ guard encoder. storage. count > depth else {
1033+ return nil
1034+ }
1035+
1036+ // return what has been created
1037+ return encoder. storage. popContainer ( )
1038+ }
1039+ }
1040+
0 commit comments