-
-
Save robertmryan/03542e2da539ad7f51c2540bafdad97e to your computer and use it in GitHub Desktop.
struct Cost: Codable { | |
let id: String | |
let label: String | |
let value: String | |
} | |
let cost = Cost(id: "something", label: "something", value: "something") | |
let jsonData = try! JSONEncoder().encode(cost) // this works fine | |
let object = try! JSONSerialization.jsonObject(with: jsonData) // this works fine |
Personally, I’d use camelCase:
struct Billing: Codable {
let firstName: String
let lastName: String
let company: String
let address1: String
let address2: String
let city: String
let state: String
let postcode: String
let country: String
let phone: String
let email: String
}
struct CouponLines: Codable {
let subtotal: String
let productId: Int
let quantity: Int
}
struct LineItem: Codable {
let subtotal: String
let productId: Int
let quantity: Int
}
struct Setting: Codable {
let cost: Cost
}
struct Cost: Codable {
let id: String
let label: String
let value: String
}
struct Shipping: Codable {
let firstName: String
let lastName: String
let address1: String
let address2: String
let state: String
let postcode: String
let country: String
}
struct ShippingLines: Codable {
let instanceId: Int
let id: Int
let title: String
let methodTitle: String
let methodId: String
let settings: Setting
}
struct NewOrder : Codable {
let billing : Billing
let couponLines : [CouponLines]?
let customerId : Int
let lineItems : [LineItem]
let paymentMethod : String
let paymentMethodTitle : String
let setPaid : Bool
let shipping : Shipping
let shippingLines : [ShippingLines]
}
And then let JSONDecoder
convert the keys for me:
let data = #"{"billing":{"phone":"۰۹۳۵۴۰۰۹۷۴۰","city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","last_name":"مقیمی","company":"","postcode":"۱۲۳۴۵۶۷۸۹","email":"","address_2":"","state":"THR","first_name":"محسن"},"set_paid":false,"line_items":[{"subtotal":"20000","product_id":158243,"quantity":1},{"subtotal":"17500","product_id":158236,"quantity":1}],"payment_method_title":"بانک سامان","shipping_lines":[{"instance_id":4,"id":4,"title":"پست سفارشی","method_title":"نرخ ثابت","settings":{"cost":{"id":"cost","label":"هزینه","value":"8900"}},"method_id":"flat_rate"}],"coupon_lines":[],"customer_id":8360,"payment_method":"WC_Saman_Gateway","shipping":{"city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","postcode":"۱۲۳۴۵۶۷۸۹","last_name":"مقیمی","address_2":"","state":"THR","first_name":"محسن"}}"#
.data(using: .utf8)!
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let object = try decoder.decode(NewOrder.self, from: data)
print("object", object)
} catch {
print(error)
}
Bottom line, I can’t reproduce your problem. It works fine for me. I took your JSON string, and decoded it fine.
thanks for your help, I tried your code but still have the problem
really?!?!?!
Even I had test it on " http://www.json4swift.com " it says that the json is not valid
Yes, take everything from that comment, paste it into Xcode playground or an app, and run it. It works fine.
Now I did have troubles when I pasted your JSON into https://jsonlint.com, validated it, and then cut and pasted the JSON that this site formatted for me into Xcode. Or you could have troubles if you converted to Data
using any encoding other than .utf8
. But that code works (which is why I included everything from all the struct
types, the JSON, and the decoding, yielding a complete MCVE.
Does it tell you where the error is? JSONSerialization
will, IIRC.
I think I found the problem, the problem is my string encoded json most be like this:
{"billing":{"phone":"۰۹۳۵۴۰۰۹۷۴۰","city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","last_name":"مقیمی","company":"","postcode":"۱۲۳۴۵۶۷۸۹","email":"","address_2":"","state":"THR","first_name":"محسن"},"set_paid":false,"line_items":[{"product_id":158243,"quantity":1,"subtotal":"20000","variation_id":null},{"product_id":158236,"quantity":1,"subtotal":"17500","variation_id":null}],"payment_method_title":"بانک سامان","shipping_lines":[{"instance_id":4,"taxes":null,"method_title":"نرخ ثابت","id":4,"method_id":"flat_rate","title":"پست سفارشی","total":null,"settings":{"min_amount":null,"cost":{"id":"cost","label":"هزینه","value":"8900"}},"total_tax":null}],"coupon_lines":[],"customer_id":8360,"payment_method":"WC_Saman_Gateway","shipping":{"city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","last_name":"مقیمی","company":null,"postcode":"۱۲۳۴۵۶۷۸۹","address_2":"","state":"THR","first_name":"محسن"}}
but its like this:
{\"billing\":{\"phone\":\"۰۹۳۵۴۰۰۹۷۴۰\",\"city\":\"جاجرود\",\"country\":\"IR\",\"address_1\":\"کوچه اول سمت راست\\t\",\"last_name\":\"مقیمی\",\"company\":\"\",\"postcode\":\"۱۲۳۴۵۶۷۸۹\",\"email\":\"\",\"address_2\":\"\",\"state\":\"THR\",\"first_name\":\"محسن\"},\"set_paid\":false,\"line_items\":[{\"product_id\":158243,\"quantity\":1,\"subtotal\":\"20000\",\"variation_id\":null},{\"product_id\":158236,\"quantity\":1,\"subtotal\":\"17500\",\"variation_id\":null}],\"payment_method_title\":\"بانک سامان\",\"shipping_lines\":[{\"instance_id\":4,\"taxes\":null,\"method_title\":\"نرخ ثابت\",\"id\":4,\"method_id\":\"flat_rate\",\"title\":\"پست سفارشی\",\"total\":null,\"settings\":{\"min_amount\":null,\"cost\":{\"id\":\"cost\",\"label\":\"هزینه\",\"value\":\"8900\"}},\"total_tax\":null}],\"coupon_lines\":[],\"customer_id\":8360,\"payment_method\":\"WC_Saman_Gateway\",\"shipping\":{\"city\":\"جاجرود\",\"country\":\"IR\",\"address_1\":\"کوچه اول سمت راست\\t\",\"last_name\":\"مقیمی\",\"company\":null,\"postcode\":\"۱۲۳۴۵۶۷۸۹\",\"address_2\":\"\",\"state\":\"THR\",\"first_name\":\"محسن\"}}
when I try to delete \ from text it transforms to this:
{billing:{phone:۰۹۳۵۴۰۰۹۷۴۰,city:جاجرود,country:IR,address_1:کوچه اول سمت راست\\t,last_name:مقیمی,company:,postcode:۱۲۳۴۵۶۷۸۹,email:,address_2:,state:THR,first_name:محسن},set_paid:false,line_items:[{product_id:158243,quantity:1,subtotal:20000,variation_id:null},{product_id:158236,quantity:1,subtotal:17500,variation_id:null}],payment_method_title:بانک سامان,shipping_lines:[{instance_id:4,taxes:null,method_title:نرخ ثابت,id:4,method_id:flat_rate,title:پست سفارشی,total:null,settings:{min_amount:null,cost:{id:cost,label:هزینه,value:8900}},total_tax:null}],coupon_lines:[],customer_id:8360,payment_method:WC_Saman_Gateway,shipping:{city:جاجرود,country:IR,address_1:کوچه اول سمت راست\\t,last_name:مقیمی,company:null,postcode:۱۲۳۴۵۶۷۸۹,address_2:,state:THR,first_name:محسن}}
how can i get raid of \ without crupting object???
this is what I had done:
let jsonData = try! JSONEncoder().encode(order)
var strJson = String(data: jsonData, encoding: String.Encoding.utf8)
the strJson is :
"{\"billing\":{\"phone\":\"۰۹۳۵۴۰۰۹۷۴۰\",\"city\":\"جاجرود\",\"country\":\"IR\",\"address_1\":\"کوچه اول سمت راست\\t\",\"last_name\":\"مقیمی\",\"company\":\"\",\"postcode\":\"۱۲۳۴۵۶۷۸۹\",\"email\":\"\",\"address_2\":\"\",\"state\":\"THR\",\"first_name\":\"محسن\"},\"set_paid\":false,\"line_items\":[{\"product_id\":158236,\"quantity\":1,\"subtotal\":\"17500\",\"variation_id\":null},{\"product_id\":158243,\"quantity\":1,\"subtotal\":\"20000\",\"variation_id\":null}],\"payment_method_title\":\"بانک سامان\",\"shipping_lines\":[{\"instance_id\":4,\"taxes\":null,\"method_title\":\"نرخ ثابت\",\"id\":4,\"method_id\":\"flat_rate\",\"title\":\"پست سفارشی\",\"total\":null,\"settings\":{\"min_amount\":null,\"cost\":{\"id\":\"cost\",\"label\":\"هزینه\",\"value\":\"8900\"}},\"total_tax\":null}],\"coupon_lines\":[],\"customer_id\":8360,\"payment_method\":\"WC_Saman_Gateway\",\"shipping\":{\"city\":\"جاجرود\",\"country\":\"IR\",\"address_1\":\"کوچه اول سمت راست\\t\",\"last_name\":\"مقیمی\",\"company\":null,\"postcode\":\"۱۲۳۴۵۶۷۸۹\",\"address_2\":\"\",\"state\":\"THR\",\"first_name\":\"محسن\"}}"
when I paste my strJson this site automatically correct the \ in my json but its actually like this:
"{\"billing\":{\"phone\":\"۰۹۳۵۴۰۰۹۷۴۰\",\"city\":\"جاجرود\",\"country\":\"IR\",\"address_1\":\"کوچه اول سمت راست\\t\",\"last_name\":\"مقیمی\",\"company\":\"\",\"postcode\":\"۱۲۳۴۵۶۷۸۹\",\"email\":\"\",\"address_2\":\"\",\"state\":\"THR\",\"first_name\":\"محسن\"},\"set_paid\":false,\"line_items\":[{\"product_id\":158236,\"quantity\":1,\"subtotal\":\"17500\",\"variation_id\":null},{\"product_id\":158243,\"quantity\":1,\"subtotal\":\"20000\",\"variation_id\":null}],\"payment_method_title\":\"بانک سامان\",\"shipping_lines\":[{\"instance_id\":4,\"taxes\":null,\"method_title\":\"نرخ ثابت\",\"id\":4,\"method_id\":\"flat_rate\",\"title\":\"پست سفارشی\",\"total\":null,\"settings\":{\"min_amount\":null,\"cost\":{\"id\":\"cost\",\"label\":\"هزینه\",\"value\":\"8900\"}},\"total_tax\":null}],\"coupon_lines\":[],\"customer_id\":8360,\"payment_method\":\"WC_Saman_Gateway\",\"shipping\":{\"city\":\"جاجرود\",\"country\":\"IR\",\"address_1\":\"کوچه اول سمت راست\\t\",\"last_name\":\"مقیمی\",\"company\":null,\"postcode\":\"۱۲۳۴۵۶۷۸۹\",\"address_2\":\"\",\"state\":\"THR\",\"first_name\":\"محسن\"}}"
The backslashes are perennial source of confusion. Let’s say you have a text string this is a "test" string
. Depending upon how you display this (e.g. if you look at in the debugger, it may show it to you as "this is a \"test\" string"
. If you see the whole thing in quotes, then quotation marks inside that quoted string are shown with \
escape character, even though that \
isn’t really in the string.
And when you create your own string literals, you have to escape the "
characters with \
(though, again, the \
character doesn’t actually appear in the string ... it’s only there for escaping):
let string = "This is a \"test\" string”
As Swift has evolved, they’ve come up with two ways to eliminate this need for escaping the quotation marks within a string. You can use the #"
and "#
pattern, e.g.
let string = #"this is a "test" string"#
Thus:
let string = #"{"billing":{"phone":"۰۹۳۵۴۰۰۹۷۴۰","city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","last_name":"مقیمی","company":"","postcode":"۱۲۳۴۵۶۷۸۹","email":"","address_2":"","state":"THR","first_name":"محسن"},"set_paid":false,"line_items":[{"subtotal":"20000","product_id":158243,"quantity":1},{"subtotal":"17500","product_id":158236,"quantity":1}],"payment_method_title":"بانک سامان","shipping_lines":[{"instance_id":4,"id":4,"title":"پست سفارشی","method_title":"نرخ ثابت","settings":{"cost":{"id":"cost","label":"هزینه","value":"8900"}},"method_id":"flat_rate"}],"coupon_lines":[],"customer_id":8360,"payment_method":"WC_Saman_Gateway","shipping":{"city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","postcode":"۱۲۳۴۵۶۷۸۹","last_name":"مقیمی","address_2":"","state":"THR","first_name":"محسن"}}"#
Or you can use the """
convention (which not only eliminates this need for escaping, but also allows you to write multi line strings):
let string = """
this is a "test" string
"""
Or
let string = """
{"billing":{"phone":"۰۹۳۵۴۰۰۹۷۴۰","city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","last_name":"مقیمی","company":"","postcode":"۱۲۳۴۵۶۷۸۹","email":"","address_2":"","state":"THR","first_name":"محسن"},"set_paid":false,"line_items":[{"subtotal":"20000","product_id":158243,"quantity":1},{"subtotal":"17500","product_id":158236,"quantity":1}],"payment_method_title":"بانک سامان","shipping_lines":[{"instance_id":4,"id":4,"title":"پست سفارشی","method_title":"نرخ ثابت","settings":{"cost":{"id":"cost","label":"هزینه","value":"8900"}},"method_id":"flat_rate"}],"coupon_lines":[],"customer_id":8360,"payment_method":"WC_Saman_Gateway","shipping":{"city":"جاجرود","country":"IR","address_1":"کوچه اول سمت راست\t","postcode":"۱۲۳۴۵۶۷۸۹","last_name":"مقیمی","address_2":"","state":"THR","first_name":"محسن"}}"#
"""
For more information, see The Swift Programming Language: String Literals.
let order = NewOrder(billing: billing, couponLine: [], customerId: customerId, lineItems: lineItems, paymentMethod: paymentMethod, paymentMethodTitle: paymentMethodTitle, setPaid: false, shipping: shipping, shippingLines: shippingLine)
let jsonData = try! JSONEncoder().encode(order)
print(JSONSerialization.isValidJSONObject(jsonData))
i was wrong, isValidJSONObject
still prints false
It’s either:
let foo = "some \"quoted\" string"
or you can use extended string delimiters with #"
and "#
:
let foo = #"some "quoted" string"#
or you can use the multiline string literal, with """
(note, three, not two "
):
let foo = """
some "quoted" string
"""
If you want to do string interpolation, it’s either:
let bar = "the value is \(foo)"
Or within the extended string delimiters, you can use \#(foo)
for interpolation:
let bar = #"the value is \#(foo)"#
Or you can use the multiline string literal with the standard string interpolation syntax:
let bar = """
the value is \(foo)
"""
let order = NewOrder(billing: billing, couponLine: [], customerId: customerId, lineItems: lineItems, paymentMethod: paymentMethod, paymentMethodTitle: paymentMethodTitle, setPaid: false, shipping: shipping, shippingLines: shippingLine) let jsonData = try! JSONEncoder().encode(order) print(JSONSerialization.isValidJSONObject(jsonData))
i was wrong,
isValidJSONObject
still printsfalse
You misunderstand the purpose of isValidJSONObject
. This method is just telling you whether you can take the object and pass it to JSONSerialization.data(withJSONObject:)
without failing. Unlike JSONEncoder
, JSONSerialization
is extremely limited in terms of what it can encode; see “Overview” section of JSONSerialization
documentation for specifics.
But you pass it an object to be encoded, not the Data
that is the raw JSON:
let foo = ["a": 1]
print(JSONSerialization.isValidJSONObject(foo)) // true
let bar = ["a", "b", "c"]
print(JSONSerialization.isValidJSONObject(bar)) // true
let baz = 42
print(JSONSerialization.isValidJSONObject(baz)) // false because it has to be array or dictionary
let qux = [NSObject()]
print(JSONSerialization.isValidJSONObject(qux)) // false because the object in the array cannot be represented
If you want to see if the Data
is valid JSON, you’ll have to actually attempt the decode...
the problem solved, it was just a type mismatch, one of the numeric parameters most be string but I send it in Integer.
thanks a lot for your helps my friend, I so sorry for wasting your time.
👍
hi again my friend, I'm have issue with my network layer, would you please help me???
Go ahead an add your question here. Worst case scenario, if it’s too complicated, I might suggest that you post it as a proper question on StackOverflow, as I’m buried right now.
I really don't know about it complication, I will ask and you take decision that its complicated or not.
I have APIClient in my project and this is the code:
protocol APIClientType {
func request<M: Codable>(_ endpoint: URLRequestConvertible) -> Single<M>
}
final class NewAPIClient: NSObject, URLSessionDelegate, APIClientType {
var session: URLSession!
override init() {
super.init()
let urlsession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue.main)
urlsession.configuration.allowsCellularAccess = true
urlsession.configuration.timeoutIntervalForRequest = TimeInterval(10.0)
self.session = urlsession
}
func request<M: Codable>(_ endpoint: URLRequestConvertible) -> Single<M> {
return Single<M>.create { [unowned self] single in
let request = endpoint.request()
#if DEBUG
print("REQUEST: \(debugPrint(request))")
#endif
let task = self.session.dataTask(with: request) { (data, response, error) in
if let responseError = error {
single(.error(responseError))
}
#if DEBUG
print("RESPONSE: \(debugPrint(response ?? "NULL"))")
#endif
if let httpResponse = response as? HTTPURLResponse {
switch httpResponse.statusCode {
case 200, 201:
guard let data = data else {
break
}
do {
let model = try JSONDecoder().decode(M.self, from: data)
single(.success(model))
} catch let myJSONError {
single(.error(myJSONError))
}
case 404:
single(.error(APIError.NotFound))
default:
break
}
}
}
task.resume()
return Disposables.create {
task.cancel()
}
}
}
}
in 2 case of web services the response is simple text not a json, something like "the task done", my issue is when I intent to get this responses the " request " function returns myJSONError, how can I modify this class to return a simple string response?
You didn’t provide all of your structs, so I recreated them using your pattern and looking at your JSON:
and I decoded with both
JSONDecoder
andJSONSerialization
without incident: