1+ import OrderedCollections
2+
13// MARK: MapError
24
35public enum MapError : Error {
@@ -14,7 +16,7 @@ public enum Map {
1416 case number( Number )
1517 case string( String )
1618 case array( [ Map ] )
17- case dictionary( [ String : Map ] )
19+ case dictionary( OrderedDictionary < String , Map > )
1820
1921 public static func int( _ value: Int ) -> Map {
2022 return . number( Number ( value) )
@@ -58,7 +60,7 @@ extension Map {
5860 self = . array( array)
5961 }
6062
61- public init ( _ dictionary: [ String : Map ] ) {
63+ public init ( _ dictionary: OrderedDictionary < String , Map > ) {
6264 self = . dictionary( dictionary)
6365 }
6466
@@ -86,7 +88,7 @@ extension Map {
8688 self = array. map ( { Map ( $0) } ) ?? . null
8789 }
8890
89- public init ( _ dictionary: [ String : Map ] ? ) {
91+ public init ( _ dictionary: OrderedDictionary < String , Map > ? ) {
9092 self = dictionary. map ( { Map ( $0) } ) ?? . null
9193 }
9294}
@@ -107,8 +109,8 @@ public func map(from value: Any?) throws -> Map {
107109 }
108110
109111 if
110- let value = value as? [ String : Any ] ,
111- let dictionary: [ String : Map ] = try ? value. reduce ( into: [ : ] , { result, pair in
112+ let value = value as? OrderedDictionary < String , Any > ,
113+ let dictionary: OrderedDictionary < String , Map > = try ? value. reduce ( into: [ : ] , { result, pair in
112114 result [ pair. key] = try map ( from: pair. value)
113115 } )
114116 {
@@ -152,7 +154,7 @@ extension Map {
152154 self = . string( string)
153155 case let array as [ Map ] :
154156 self = . array( array)
155- case let dictionary as [ String : Map ] :
157+ case let dictionary as OrderedDictionary < String , Map > :
156158 self = . dictionary( dictionary)
157159 default :
158160 throw MapError . incompatibleType
@@ -243,7 +245,7 @@ extension Map {
243245 return try ? arrayValue ( )
244246 }
245247
246- public var dictionary : [ String : Map ] ? {
248+ public var dictionary : OrderedDictionary < String , Map > ? {
247249 return try ? dictionaryValue ( )
248250 }
249251}
@@ -372,7 +374,7 @@ extension Map {
372374 }
373375 }
374376
375- public func dictionaryValue( converting: Bool = false ) throws -> [ String : Map ] {
377+ public func dictionaryValue( converting: Bool = false ) throws -> OrderedDictionary < String , Map > {
376378 guard converting else {
377379 return try get ( )
378380 }
@@ -487,7 +489,7 @@ extension Map {
487489 if let existingDictionary = dictionary [ key] ? . dictionary,
488490 let newDictionary = newValue. dictionary,
489491 merging {
490- var combinedDictionary : [ String : Map ] = [ : ]
492+ var combinedDictionary : OrderedDictionary < String , Map > = [ : ]
491493
492494 for (key, value) in existingDictionary {
493495 combinedDictionary [ key] = value
@@ -617,8 +619,20 @@ extension Map : Codable {
617619 else if let array = try ? container. decode ( [ Map ] . self) {
618620 self = . array( array)
619621 }
620-
621- else if let dictionary = try ? container. decode ( [ String : Map ] . self) {
622+
623+ else if let _ = try ? container. decode ( [ String : Map ] . self) {
624+ // Override OrderedDictionary default (unkeyed alternating key-value)
625+ // Instead decode as a keyed container (like normal Dictionary) but use the order of the input
626+ let container = try decoder. container ( keyedBy: _DictionaryCodingKey. self)
627+ var orderedDictionary : OrderedDictionary < String , Map > = [ : ]
628+ for key in container. allKeys {
629+ let value = try container. decode ( Map . self, forKey: key)
630+ orderedDictionary [ key. stringValue] = value
631+ }
632+ self = . dictionary( orderedDictionary)
633+ }
634+
635+ else if let dictionary = try ? container. decode ( OrderedDictionary< String, Map> . self ) {
622636 self = . dictionary( dictionary)
623637 }
624638
@@ -642,9 +656,32 @@ extension Map : Codable {
642656 case let . array( array) :
643657 try container. encode ( array)
644658 case let . dictionary( dictionary) :
645- try container. encode ( dictionary)
659+ // Override OrderedDictionary default (unkeyed alternating key-value)
660+ // Instead decode as a keyed container (like normal Dictionary) in the order of our OrderedDictionary
661+ var container = encoder. container ( keyedBy: _DictionaryCodingKey. self)
662+ for (key, value) in dictionary {
663+ let codingKey = _DictionaryCodingKey ( stringValue: key) !
664+ try container. encode ( value, forKey: codingKey)
665+ }
646666 }
647667 }
668+
669+ /// A wrapper for dictionary keys which are Strings or Ints.
670+ /// This is copied from Swift core: https://github.com/apple/swift/blob/256a9c5ad96378daa03fa2d5197b4201bf16db27/stdlib/public/core/Codable.swift#L5508
671+ internal struct _DictionaryCodingKey : CodingKey {
672+ internal let stringValue : String
673+ internal let intValue : Int ?
674+
675+ internal init ? ( stringValue: String ) {
676+ self . stringValue = stringValue
677+ self . intValue = Int ( stringValue)
678+ }
679+
680+ internal init ? ( intValue: Int ) {
681+ self . stringValue = " \( intValue) "
682+ self . intValue = intValue
683+ }
684+ }
648685}
649686// MARK: Equatable
650687
@@ -738,7 +775,7 @@ extension Map : ExpressibleByArrayLiteral {
738775
739776extension Map : ExpressibleByDictionaryLiteral {
740777 public init ( dictionaryLiteral elements: ( String , Map ) ... ) {
741- var dictionary = [ String: Map] ( minimumCapacity: elements. count)
778+ var dictionary = OrderedDictionary < String , Map > ( minimumCapacity: elements. count)
742779
743780 for (key, value) in elements {
744781 dictionary [ key] = value
@@ -847,7 +884,7 @@ extension Map {
847884 }
848885 }
849886
850- func serialize( dictionary: [ String : Map ] ) -> String {
887+ func serialize( dictionary: OrderedDictionary < String , Map > ) -> String {
851888 var string = " { "
852889 var index = 0
853890
0 commit comments