Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vapor JSON from `[String: Any]` Dictionary

Tags:

json

swift

vapor

If I build a Swift dictionary, i.e. [String: Any] how can I return that as JSON? I tried this, but it gives me the error: Argument labels '(node:)' do not match any available overloads.

drop.get("test") { request in
    var data: [String: Any] = [:]

    data["name"] = "David"
    data["state"] = "CA"

    return try JSON(node: data)
}
like image 952
keegan3d Avatar asked Apr 18 '26 05:04

keegan3d


2 Answers

Convoluted as heck, but this allows you to use [String:Any].makeNode(), as long as the internals are NodeRepresentable, NSNumber based, or NSNull :) --

import Node

enum NodeConversionError : LocalizedError {
    case invalidValue(String,Any)
    var errorDescription: String? {
        switch self {
        case .invalidValue(let key, let value): return "Value for \(key) is not NodeRepresentable - " + String(describing: type(of: value))
        }
    }
}

extension NSNumber : NodeRepresentable {
    public func makeNode(context: Context = EmptyNode) throws -> Node {
        return Node.number(.double(Double(self)))
    }
}

extension NSString : NodeRepresentable {
    public func makeNode(context: Context = EmptyNode) throws -> Node {
        return Node.string(String(self))
    }
}

extension KeyAccessible where Key == String, Value == Any {
    public func makeNode(context: Context = EmptyNode) throws -> Node {
        var mutable: [String : Node] = [:]
        try allItems.forEach { key, value in
            if let _ = value as? NSNull {
                mutable[key] = Node.null
            } else {
                guard let nodeable = value as? NodeRepresentable else { throw NodeConversionError.invalidValue(key, value) }
                mutable[key] = try nodeable.makeNode()
            }
        }
        return .object(mutable)
    }

    public func converted<T: NodeInitializable>(to type: T.Type = T.self) throws -> T {
        return try makeNode().converted()
    }
}

With that header you can:

return try JSON(node: data.makeNode())
like image 105
BadPirate Avatar answered Apr 20 '26 09:04

BadPirate


JSON cannot be initialized from a [String : Any] dictionary because Any is not convertible to Node.

There are only a limited number of types that Node can be. (See Node source). If you know your objects are all going to be the same type, use a dictionary that only allows that type. So for your example, [String : String].

If you're going to be getting data from the request, you can try using request.json as is used in the documentation here.

EDIT:

Another (possibly better) solution would be to make your dictionary [String: Node] and then you can include any type that conforms to Node. You may have to call the object's makeNode() function to add it to the dictionary though.

like image 42
Ponyboy47 Avatar answered Apr 20 '26 09:04

Ponyboy47