1
+ import OrderedCollections
2
+
1
3
// MARK: MapError
2
4
3
5
public enum MapError : Error {
@@ -14,7 +16,7 @@ public enum Map {
14
16
case number( Number )
15
17
case string( String )
16
18
case array( [ Map ] )
17
- case dictionary( [ String : Map ] )
19
+ case dictionary( OrderedDictionary < String , Map > )
18
20
19
21
public static func int( _ value: Int ) -> Map {
20
22
return . number( Number ( value) )
@@ -58,7 +60,7 @@ extension Map {
58
60
self = . array( array)
59
61
}
60
62
61
- public init ( _ dictionary: [ String : Map ] ) {
63
+ public init ( _ dictionary: OrderedDictionary < String , Map > ) {
62
64
self = . dictionary( dictionary)
63
65
}
64
66
@@ -86,7 +88,7 @@ extension Map {
86
88
self = array. map ( { Map ( $0) } ) ?? . null
87
89
}
88
90
89
- public init ( _ dictionary: [ String : Map ] ? ) {
91
+ public init ( _ dictionary: OrderedDictionary < String , Map > ? ) {
90
92
self = dictionary. map ( { Map ( $0) } ) ?? . null
91
93
}
92
94
}
@@ -107,8 +109,8 @@ public func map(from value: Any?) throws -> Map {
107
109
}
108
110
109
111
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
112
114
result [ pair. key] = try map ( from: pair. value)
113
115
} )
114
116
{
@@ -152,7 +154,7 @@ extension Map {
152
154
self = . string( string)
153
155
case let array as [ Map ] :
154
156
self = . array( array)
155
- case let dictionary as [ String : Map ] :
157
+ case let dictionary as OrderedDictionary < String , Map > :
156
158
self = . dictionary( dictionary)
157
159
default :
158
160
throw MapError . incompatibleType
@@ -243,7 +245,7 @@ extension Map {
243
245
return try ? arrayValue ( )
244
246
}
245
247
246
- public var dictionary : [ String : Map ] ? {
248
+ public var dictionary : OrderedDictionary < String , Map > ? {
247
249
return try ? dictionaryValue ( )
248
250
}
249
251
}
@@ -372,7 +374,7 @@ extension Map {
372
374
}
373
375
}
374
376
375
- public func dictionaryValue( converting: Bool = false ) throws -> [ String : Map ] {
377
+ public func dictionaryValue( converting: Bool = false ) throws -> OrderedDictionary < String , Map > {
376
378
guard converting else {
377
379
return try get ( )
378
380
}
@@ -487,7 +489,7 @@ extension Map {
487
489
if let existingDictionary = dictionary [ key] ? . dictionary,
488
490
let newDictionary = newValue. dictionary,
489
491
merging {
490
- var combinedDictionary : [ String : Map ] = [ : ]
492
+ var combinedDictionary : OrderedDictionary < String , Map > = [ : ]
491
493
492
494
for (key, value) in existingDictionary {
493
495
combinedDictionary [ key] = value
@@ -617,8 +619,20 @@ extension Map : Codable {
617
619
else if let array = try ? container. decode ( [ Map ] . self) {
618
620
self = . array( array)
619
621
}
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 ) {
622
636
self = . dictionary( dictionary)
623
637
}
624
638
@@ -642,9 +656,32 @@ extension Map : Codable {
642
656
case let . array( array) :
643
657
try container. encode ( array)
644
658
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
+ }
646
666
}
647
667
}
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
+ }
648
685
}
649
686
// MARK: Equatable
650
687
@@ -738,7 +775,7 @@ extension Map : ExpressibleByArrayLiteral {
738
775
739
776
extension Map : ExpressibleByDictionaryLiteral {
740
777
public init ( dictionaryLiteral elements: ( String , Map ) ... ) {
741
- var dictionary = [ String: Map] ( minimumCapacity: elements. count)
778
+ var dictionary = OrderedDictionary < String , Map > ( minimumCapacity: elements. count)
742
779
743
780
for (key, value) in elements {
744
781
dictionary [ key] = value
@@ -847,7 +884,7 @@ extension Map {
847
884
}
848
885
}
849
886
850
- func serialize( dictionary: [ String : Map ] ) -> String {
887
+ func serialize( dictionary: OrderedDictionary < String , Map > ) -> String {
851
888
var string = " { "
852
889
var index = 0
853
890
0 commit comments