keegan3d
keegan3d

Reputation: 11275

Vapor JSON from `[String: Any]` Dictionary

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)
}

Upvotes: 3

Views: 1614

Answers (2)

BadPirate
BadPirate

Reputation: 26177

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())

Upvotes: 2

Ponyboy47
Ponyboy47

Reputation: 939

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.

Upvotes: 1

Related Questions