My question is what difference in Vapor JSON function calls return JSON(["foo":"bar"]) vs return try JSON(node: ["foo":"bar"])?
Both variants work, what is the right way?
Mixing them like return JSON(node: ["foo":"bar"]) or return try JSON(["foo":"bar"]) will make build fail.
import Vapor
let drop = Droplet()
drop.get("json") { req in
return JSON(["foo": "bar"])
}
drop.run()
I think I can answer this one. At first glance, these look pretty similar, but they're very different. The singular reason that everything comes back to in these two initializers is ... GENERICS.
No External Arg
No external arg initializer refers to JSON(["foo": "bar"]) above. We use these for non-failable initializers for types that can be directly represented in JSON. For example, [String: JSON], String, [JSON], Number(Int, UInt, Double), etc..
You may say, "wait a minute, I'm passing [String: String] above. Well, here's the thing ... actually we're not. JSON is ExpressibleAsStringLiteral so ["foo": "bar"] above actually becomes ["foo": JSON("bar")] and allows us to use the no-argument initializer.
With External Arg node:
We use the external argument to help the compiler disambiguate since we were unable to use the same external parameters for failable and non-failable initializers.
If you look through the node: initializer, it is a set of generic overloads that allow us to make things easier. As above we mentioned that we can pass ["foo": "bar"] directly because it converts to [String: JSON]. Well, if we have a type that is concretely [String: String] and we try to use JSON(stringDict) it'll fail. If however we use try JSON(node: stringDict) we can use generics to know that String is NodeRepresentible and we have enough context to properly convert it to JSON. Or at least try to!
By having the node: initializer, we can allow multiple different generic variants and work with multiple different types.
:)
Hope this clears some things up, this is a pretty nuanced area of the code base, happy to elaborate more.
Related
I want to wrap data to JSON before sending.
func request(content: [String]) {
try? JSONEncoder().encode(content)
This worked well when sending arrays of string. However, I would also like to send other structures that can be represented in JSON.
For example arrays containing arrays, or dictionaries.
My idea was to make it
func request(content: Any) {
try? JSONEncoder().encode(content)
Which gives Protocol 'Any' as a type cannot conform to 'Encodable'
How can I make the parameter as generic as possible so what can be formed as JSON gets formed as JSON and whatever fails, fails? I do understand I can't JSONify everything but I would like to be able to do it with things that I know of can be represented in JSON form. I found things like this https://stackoverflow.com/a/64471720/2161301 but apparently you can't do that on "normal" functions
You can use AnyCodable in your project.
Then you will be allowed to use [String: AnyEncodable] / [AnyEncodable] etc. in the places where you are not able to use Any while trying to use JSONEncoder api.
Is it possible to reconstruct a JSON object in XQuery? Using XML, it's possible to use computed constructors to rebuild an element:
element { node-name($some-element) } {
(: Do stuff with $some-element/(#*|node()) :)
}
But using JSON objects, it seems that it's not possible to reconstruct properties. I would like to do something like this, but this throws a syntax error:
object-node {
for $p in $some-json-object/*
return node-name($p) : $p
}
It looks like it's possible to workaround that by mutating the JSON object:
let $obj := json:object(document{xdmp:from-json($json)}/*)
let $_put := map:put($o, 'prop-name', $prop-val)
return xdmp:to-json($o)/node()
But this has some obvious limitations.
I'm afraid using json:object really is the way to use here. Could be worse though, you only need a few lines to copy all json properties. You also don't need that document{} constructor, nor the extra type cast to json:object. xdmp:from-json already returns a json:object:
let $org := xdmp:from-json($json)
let $new := json:object()
let $_ :=
for $key in map:keys($org)
return map:put($new, $key, map:get($org, $key))
return xdmp:to-json($new)/node()
HTH!
This may be helpful for you: http://docs.marklogic.com/guide/app-dev/json
However, I often take a different approach in xQuery (being comfortable with XML). This may get some push-back from people here, but it is my approach:
Construct what you like in XML and then transform it. If you make your XML in the http: //marklogic.com/xdmp/json/basic namespace, then you can just transform it to whatever complex JSON you desire using json:transform-to-json - since all of the hints to datatypes are in the attributes of the XML. The nice thing about this approach is that it is a nice middle format. I can transform to JSON - or I can apply an XSLT transformation and get other XML if I desire.
It should be noted that json:transform-to-json has other modes of operation and can get datatype hints from your own schema as well. But I perfer the built-in schema.
I stumbled across this blog post by #paxstonhare that uses a non-functional approach, rebuilding new JSON objects during the tree walk by mutating them using map:put():
http://developer.marklogic.com/blog/walking-among-the-json-trees
I am new on Swift, my question is where do we use and need External Parameter?
From the Apple's Swift Language Guide:
Sometimes it’s useful to name each parameter when you call a function,
to indicate the purpose of each argument you pass to the function.
If you want users of your function to provide parameter names when
they call your function, define an external parameter name for each
parameter, in addition to the local parameter name.
So, you don't "need" an external parameter name but it is a good practice to use them because they serve as documentation about the parameters at the point the method is called.
For example, without using external parameter names, you can define a join method like this:
func join(_ s1: String,_ s2: String,_ joiner: String) -> String {
return s1 + joiner + s2
}
which will then be called like this:
join("foo", "bar", ", ")
As you can see, each parameter's meaning is not very clear.
Using external parameter names, you could define the same method like below:
func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String {
return s1 + joiner + s2
}
which would then force the users to call it like this:
join(string: "foo", toString: "bar", withJoiner: ", ")
You can see that it makes the meaning of the parameters, along with what the method does, much more clear.
It might seem not so important in this simple example but when defining methods that take a lot of parameters with not-so-obvious meanings, using external parameter names will make your code much more easy to understand.
Update for Swift 3:
This has become even more meaningful with the introduction of Swift 3. Consider the append(contentsOf:) method of the Array class in Swift 3:
Not having different internal and external parameter names in this case would force us to change the label contentsOf to something like string in the call site, which wouldn't read as good as the former one. Swift 3 API guidelines rely on having different internal and external parameter names to create clear and concise methods.
I've run into a use case where I need to parse a bunch of information from a txt file. The meat of the payload is a bunch of key:value pairs, none of which I know at development time.
Wombat
Area: "Northern Alberta"
Tank Level (ft): -3.395
Temperature (C): 19.3
Batt Voltage: 13.09
Last Maintained: 2012-01-01
Secured: "Yes"
As you can see, there is potential for Strings, Numbers, Dates and Booleans. There is also a use case where the user needs to create rules for certain attributes, things like:
When the Tank Level exceeds n, please notify some.user#someplace.com
When a Site that contains "Alberta" is Not Secured, please notify some.user#someplace.com
Depending on the type of attribute, the available rule types will differ. I may also need to do some kind of aggregation on the numeric types. Anyway, to make a long story short, I need the type information. So what kind of data structure is best?
Initially I was going to go with distinct tuples.
val stringAttributes: Array[(String, String)]
val doubleAttributes: Array[(String, Double)]
val dateAttributes: Array[(String, Date)]
Now that seems wrong, or at the very least ugly. Then I though maybe something like:
val attributes: Array[(String, Any)]
Now I have a pattern match in many of places. Also note I'm using a JSON protocol for the web application and database (MongoDB). It'd be convenient to give the front end something like this:
{
site: "Wombat",
attributes: [
{ "Area": "Northern Alberta" },
{ "Tank Level (ft)": -3.395 },
{ "Temperature (C)": 19.3 }
]
}
But in the back end, do I encode the types? Do I parse raw JSON? In the end, I'm looking for the best way to maintain type information for dynamic set of attributes while supporting JSON to both the web client and the database.
+1 on #david's comment.
Why not use JSON end-to-end and derive the type desired for each individual rule? Then use a single function to transform a JSON value to the correct type (using defaults or Option when it doesn't really match).
Then you can use something like this to run the ruleset over all of the parsed objects:
for (object <- parseObjects(); rule <- rules) {
val value = coerce(object, rule.desiredType)
rule(value)
}
Or something similar, such as having the rule itself call the coercion code. It seems like you don't really need the objects to have a type until you're doing rule processing, so why try to?
P.S.
Typesafe Config may be able to parse the file for you already, although it looks to be a simple format to parse.
I'm considering porting a very simple text-templating library to scala, mostly as an exercise in learning the language. The library is currently implemented in both Python and Javascript, and its basic operation more or less boils down to this (in python):
template = CompiledTemplate('Text {spam} blah {eggs[1]}')
data = { 'spam': 1, 'eggs': [ 'first', 'second', { 'key': 'value' }, true ] }
output = template.render(data)
None of this is terribly difficult to do in Scala, but the thing I'm unclear about is how to best express the static type of the data parameter.
Basically this parameter should be able to contain the sorts of things you'd find in JSON: a few primitives (strings, ints, booleans, null), or lists of zero or more items, or maps of zero or more items. (For the purposes of this question the maps can be constrained to having string keys, which seems to be how Scala likes things anyways.)
My initial thought was just to use a Map[string, Any] as a top-level object, but that's doesn't seem entirely correct to me. In fact I don't want to add arbitrary objects of any sort of class in there; I want only the elements I outlined above. At the same time, I think in Java the closest I'd really be able to get would be Map<String, ?>, and I know one of the Scala authors designed Java's generics.
One thing I'm particularly curious about is how other functional languages with similar type systems handle this sort of problem. I have a feeling that what I really want to do here is come up with a set of case classes that I can pattern-match on, but I'm not quite able to envision how that would look.
I have Programming in Scala, but to be honest my eyes started glazing over a bit at the covariance / contravariance stuff and I'm hoping somebody can explain this to me a bit more clearly and succinctly.
You're spot on that you want some sort of case classes to model your datatypes. In functional languages these sorts of things are called "Abstract Data Types", and you can read all about how Haskell uses them by Googling around a bit. Scala's equivalent of Haskell's ADTs uses sealed traits and case classes.
Let's look at a rewrite of the JSON parser combinator from the Scala standard library or the Programming in Scala book. Instead of using Map[String, Any] to represent JSON objects, and instead of using Any to represent arbitrary JSON values, it uses an abstract data type, JsValue, to represnt JSON values. JsValue has several subtypes, representing the possible kinds of JSON values: JsString, JsNumber, JsObject, JsArray, JsBoolean (JsTrue, JsFalse), and JsNull.
Manipulating JSON data of this form involves pattern matching. Since the JsValue is sealed, the compiler will warn you if you haven't dealt with all the cases. For example, the code for toJson, a method that takes a JsValue and returns a String representation of that values, looks like this:
def toJson(x: JsValue): String = x match {
case JsNull => "null"
case JsBoolean(b) => b.toString
case JsString(s) => "\"" + s + "\""
case JsNumber(n) => n.toString
case JsArray(xs) => xs.map(toJson).mkString("[",", ","]")
case JsObject(m) => m.map{case (key, value) => toJson(key) + " : " + toJson(value)}.mkString("{",", ","}")
}
Pattern matching both lets us make sure we're dealing with every case, and also "unwraps" the underlying value from its JsType. It provides a type-safe way of knowing that we've handled every case.
Furthermore, if you know at compile-time the structure of the JSON data you're dealing with, you can do something really cool like n8han's extractors. Very powerful stuff, check it out.
Well, there are a couple ways to approach this. I would probably just use Map[String, Any], which should work just fine for your purposes (as long as the map is from collection.immutable rather than collection.mutable). However, if you really want to go through some pain, it is possible to give a type for this:
sealed trait InnerData[+A] {
val value: A
}
case class InnerString(value: String) extends InnerData[String]
case class InnerMap[A, +B](value: Map[A, B]) extends InnerData[Map[A, B]]
case class InnerBoolean(value: Boolean) extends InnerData[Boolean]
Now, assuming that you were reading the JSON data field into a Scala field named jsData, you would give that field the following type:
val jsData: Map[String, Either[Int, InnerData[_]]
Every time you pull a field out of jsData, you would need to pattern match, checking whether the value was of type Left[Int] or Right[InnerData[_]] (the two sub-types of Either[Int, InnerData[_]]). Once you have the inner data, you would then pattern match on that to determine whether it represents an InnerString, InnerMap or InnerBoolean.
Technically, you have to do this sort of pattern matching anyway in order to use the data once you pull it out of JSON. The advantage to the well-typed approach is the compiler will check you to ensure that you haven't missed any possibilities. The disadvantage is that you can't just skip impossibilities (like 'eggs' mapping to an Int). Also, there is some overhead imposed by all of these wrapper objects, so watch out for that.
Note that Scala does allow you to define a type alias which should cut down on the amount of LoC required for this:
type DataType[A] = Map[String, Either[Int, InnerData[A]]]
val jsData: DataType[_]
Add a few implicit conversions to make the API pretty, and you should be all nice and dandy.
JSON is used as an example in "Programming in Scala", in the chapter on combinator parsing.