1

I need make a request to a server of an open API. The rule of the open API asked json parameter must make keys as a special order(not the A-Z order nor Z-A order).

struct Req : Encodable {
    let SourceText: String
    let Target: String
    let Source: String
    let ProjectId: Int = 0
}

What I want is:

{"SourceText":"你好","Source":"zh","Target":"en","ProjectId":0}

But the encoded json is:

{"ProjectId":0,"Source":"zh","SourceText":"你好","Target":"en"}
boybeak
  • 417
  • 5
  • 19
  • 1
    That's a bad API though... I'm afraid you need to create a custom JSONEncoder. – Larme Oct 24 '22 at 09:18
  • Since the code is open, you could mimic that https://github.com/apple/swift-corelibs-foundation/blob/de5f7edc3040057717f7da0ba875df04d3dc3e9e/Sources/Foundation/JSONEncoder.swift#L1012 You could put in `userInfo` the keys order you want, and replicate it when writing. – Larme Oct 24 '22 at 09:20
  • 2
    A dictionary is unordered by definition, hence most JSON encoders/decoders don't actually support ordered encoding/decoding. If your API relies on a fixed order of a dictionary, it is a bad API and should be changed. – Dávid Pásztor Oct 24 '22 at 09:44
  • Or create the json string manually – Joakim Danielson Oct 24 '22 at 11:05
  • I tried a json string, and send the request with alamofire with no encoders, it gives me an error: `failure(Alamofire.AFError.parameterEncoderFailed(reason: Alamofire.AFError.ParameterEncoderFailureReason.encoderFailed(error: Alamofire.URLEncodedFormEncoder.Error.invalidRootObject("string(\"{\\\"SourceText\\\":\\\"你好\\\",\\\"Source\\\":\\\"zh\\\",\\\"Target\\\":\\\"en\\\",\\\"ProjectId\\\":0}\")"))))` – boybeak Oct 24 '22 at 13:56
  • You can use an OrderedDictionary. See https://stackoverflow.com/a/76344268/2974621 – Chris May 26 '23 at 21:31

2 Answers2

0

With JSONEncoder i think that isn't possible.

One possible workaround is to use key placeholders, e.g. keyA, keyB, keyC, let them sort them alphabetically and then just replace them using a dictionary in the resulting JSON from this question I hope is helpful.

However, JSON is:

"An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a string, number, boolean, null, object, or array."

RFC 7159

Luca Petra
  • 146
  • 6
0

One workaround here that is ok if you don't have to many properties or custom types is to build a string containing the json and then convert it to Data

extension Req {
    var jsonString: String {
        String(format: #"{""SourceText": "%@, "Source": %@, "Target": %@, "ProjectId": %d}"#,
               SourceText, Source, Target, ProjectId)
    }
}

or directly returning Data?

extension Req {
    var json: Data? {
        String(format: #"{""SourceText": "%@, "Source": %@, "Target": %@, "ProjectId": %d}"#,
               SourceText, Source, Target, ProjectId).data(using: .utf8)
    }
}
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52