Serializing an F# function - f#

Using F# I've some configuration that I want to serialize to disk using Newtonsoft.Json. The config data is a record type. One of the fields in the record type is a function of type string->bool and is used to compare a string to a given value. e.g.
(fun (x:string) -> x = "1")
Serializing the type succeeds but the field isn't successfully stored (it is recorded as '{}') and therefore deserializing fails. This seems to be by design.
How do I store a function so that it can be used to populate the record type from disk when deserialized and still be executable?
I've looked at quotations as a means of storing expressions as data but I'm not sure if this is the way to go. If it is then I'm struggling to get this working.
How can achieve what I need?
Update;
type Logic = string -> bool
The record type storing config;
type Exclusion =
| FromDataValue of QItem * Logic * ExcludedItems
| FromDataValuesAnd of (QItem * Logic) seq * ExcludedItems
| FromDataValuesOr of (QItem * Logic) seq seq * ExcludedItems
The Exclusion record is populated by a user and runs against some data sets excluding items from the return collection. The QItem represents a string in the dataset that the Logic function is applied to, and if returns true the items in ExcludedItems are excluded from the results.
Initially the config is created by a user within the solution so all works fine. However once a config is created I want to be able to save the config to disc so that it can be loaded and run again when required.
So I need to be able to store this, but I want to be able to store it as a string then run it again when loaded.

You can serialize a function to JSON and then deserialize it and execute the function later using FSPickler:
open MBrace.FsPickler
open MBrace.FsPickler.Json
open System.IO
open System.Text
let serializer = JsonSerializer(indent = true)
let utf8 = UTF8Encoding(false)
let toJson (x: 'a) =
use stream = new MemoryStream()
serializer.Serialize(stream, x)
stream.ToArray() |> utf8.GetString
let parseJson<'a> json =
use reader = new StringReader(json)
serializer.Deserialize<'a>(reader)
let f = fun x -> x + 1
let serialized = toJson f
let deserialized = parseJson<int -> int> serialized
deserialized 1 // returns 2
The serialized JSON for the function looks like this:
{
"FsPickler": "4.0.0",
"type": "Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32]",
"value": {
"_flags": "subtype",
"subtype": {
"Case": "NamedType",
"Name": "FSI_0002+serialized#23",
"Assembly": {
"Name": "FSI-ASSEMBLY",
"Version": "0.0.0.0",
"Culture": "neutral",
"PublicKeyToken": ""
}
},
"instance": {}
}
}
Although instance is blank, it records the metadata about the anonymous type created for the function. That way, it can invoke the correct code when you call the deserialized version, as long as the function type is available in the AppDomain where you do the deserialization.
EDIT
If you want to literally serialize the logic for the function, you can use FSPickler to serialize a code quotation instead:
open MBrace.FsPickler
open MBrace.FsPickler.Json
open FSharp.Quotations
open FSharp.Quotations.Evaluator
open System.IO
open System.Text
let serializer = JsonSerializer(indent = true)
let utf8 = UTF8Encoding(false)
let toJson (x: 'a) =
use stream = new MemoryStream()
serializer.Serialize(stream, x)
stream.ToArray() |> utf8.GetString
let parseJson<'a> json =
use reader = new StringReader(json)
serializer.Deserialize<'a>(reader)
let f = <# fun x -> x + 1 #>
let serialized = toJson f
let deserialized = parseJson<Expr<int -> int>> serialized
let increment = deserialized |> QuotationEvaluator.Evaluate
increment 1
This way, the quotation gets serialized to JSON with all the logic described as an expression tree, and when you deserialize it you can use the FSharp.Quotations.Evaluator library to turn it into a runnable function that you can invoke.
The JSON is now considerably larger, but this can be deserialized and evaluated anywhere:
{
"FsPickler": "4.0.0",
"type": "Microsoft.FSharp.Quotations.FSharpExpr`1[Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32]]",
"value": {
"attribs": [
{
"attribs": [],
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "NewTupleOp",
"Item": {
"Case": "GenericTypeInstance",
"GenericDefinition": {
"Case": "NamedType",
"Name": "System.Tuple`2",
"Assembly": {
"Name": "mscorlib",
"Version": "4.0.0.0",
"Culture": "neutral",
"PublicKeyToken": "b77a5c561934e089"
}
},
"TypeArgs": [
{
"Case": "NamedType",
"Name": "System.String",
"Assembly": {
"_flags": "cached",
"id": 9
}
},
{
"Case": "GenericTypeInstance",
"GenericDefinition": {
"Case": "NamedType",
"Name": "System.Tuple`5",
"Assembly": {
"_flags": "cached",
"id": 9
}
},
"TypeArgs": [
{
"_flags": "cached",
"id": 11
},
{
"Case": "NamedType",
"Name": "System.Int32",
"Assembly": {
"_flags": "cached",
"id": 9
}
},
{
"_flags": "cached",
"id": 15
},
{
"_flags": "cached",
"id": 15
},
{
"_flags": "cached",
"id": 15
}
]
}
]
}
},
"Item2": [
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "ValueOp",
"Item1": {
"_flags": "subtype",
"subtype": {
"_flags": "cached",
"id": 11
},
"instance": "DebugRange"
},
"Item2": {
"_flags": "cached",
"id": 11
},
"Item3": null
},
"Item2": {
"_flags": "cached",
"id": 4
}
}
},
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "NewTupleOp",
"Item": {
"_flags": "cached",
"id": 12
}
},
"Item2": [
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "ValueOp",
"Item1": {
"_flags": "subtype",
"subtype": {
"_flags": "cached",
"id": 11
},
"instance": "C:\\Users\\aeshbach\\AppData\\Local\\Temp\\~vs220A.fsx"
},
"Item2": {
"_flags": "cached",
"id": 11
},
"Item3": null
},
"Item2": {
"_flags": "cached",
"id": 4
}
}
},
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "ValueOp",
"Item1": {
"_flags": "subtype",
"subtype": {
"_flags": "cached",
"id": 15
},
"instance": 32
},
"Item2": {
"_flags": "cached",
"id": 15
},
"Item3": null
},
"Item2": {
"_flags": "cached",
"id": 4
}
}
},
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "ValueOp",
"Item1": {
"_flags": "subtype",
"subtype": {
"_flags": "cached",
"id": 15
},
"instance": 11
},
"Item2": {
"_flags": "cached",
"id": 15
},
"Item3": null
},
"Item2": {
"_flags": "cached",
"id": 4
}
}
},
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "ValueOp",
"Item1": {
"_flags": "subtype",
"subtype": {
"_flags": "cached",
"id": 15
},
"instance": 32
},
"Item2": {
"_flags": "cached",
"id": 15
},
"Item3": null
},
"Item2": {
"_flags": "cached",
"id": 4
}
}
},
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "ValueOp",
"Item1": {
"_flags": "subtype",
"subtype": {
"_flags": "cached",
"id": 15
},
"instance": 25
},
"Item2": {
"_flags": "cached",
"id": 15
},
"Item3": null
},
"Item2": {
"_flags": "cached",
"id": 4
}
}
}
]
}
}
]
}
}
],
"term": {
"Case": "LambdaTerm",
"Item1": {
"isMutable104": false,
"name": "x",
"stamp": 0,
"typ": {
"_flags": "cached",
"id": 15
}
},
"Item2": {
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "StaticMethodCallOp",
"Item": {
"Case": "GenericMethodInstance",
"GenericDefinition": {
"Case": "Method",
"Signature": "T3 op_Addition[T1,T2,T3](T1,T2)",
"IsStatic": true,
"DeclaringType": {
"Case": "NamedType",
"Name": "Microsoft.FSharp.Core.Operators",
"Assembly": {
"Name": "FSharp.Core",
"Version": "4.4.1.0",
"Culture": "neutral",
"PublicKeyToken": "b03f5f7f11d50a3a"
}
},
"ReflectedType": null
},
"TypeArgs": [
{
"_flags": "cached",
"id": 15
},
{
"_flags": "cached",
"id": 15
},
{
"_flags": "cached",
"id": 15
}
]
}
},
"Item2": [
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "VarTerm",
"Item": {
"_flags": "cached",
"id": 40
}
}
},
{
"attribs": {
"_flags": "cached",
"id": 4
},
"term": {
"Case": "CombTerm",
"Item1": {
"Case": "ValueOp",
"Item1": {
"_flags": "subtype",
"subtype": {
"_flags": "cached",
"id": 15
},
"instance": 1
},
"Item2": {
"_flags": "cached",
"id": 15
},
"Item3": null
},
"Item2": {
"_flags": "cached",
"id": 4
}
}
}
]
}
}
}
}
}

Related

iOS JSON decoder data in not in correct format error

I'm building a simple app which fetches json data from server and present it to user.I have a json file in vapor Public directory and when I try to parse it in iOS app it gives an error data is in incorrect format.I have converted the json file to swift struct by using json to swift online convertor.
But when I test the server response with postman it gives me the json file.In iOS app it gives me an error.I was able to fetch the data if I print it it gives 1372 bytes but when I try to parse it gives me an error that data is incorrect format
I'm getting following error
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))
my json file
{
"memes": [
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-53-300x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-53.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-1-300x210.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-1.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-54-300x250.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-54.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-55-300x269.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-55.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-56.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-56.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-57.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-57.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-58.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-58.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-8.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-8.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-59.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-59.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-66.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-66.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/download.jpg",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/download.jpg"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-12-300x281.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-12.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-13-300x281.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-13.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-14-240x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-14.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-61.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-61.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-62.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-62.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-17-300x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-17.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-18-252x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-18.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-19-300x281.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-19.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-20-249x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-20.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-21-300x233.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-21.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-63.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-63.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-64.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-64.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-24-300x150.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-24.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-25-289x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-25.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-27.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-27.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-52-300x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-52.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-30-300x221.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-30.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-65.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-65.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-32-300x239.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-32.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-33-300x181.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-33.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-34-300x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-34.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-35-300x250.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-35.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-36-300x269.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-36.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-37-300x210.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-37.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-38-300x292.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-38.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-39-300x167.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-39.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-40-249x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-40.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-41-300x294.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-41.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-42-300x295.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-42.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-43-234x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-43.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-44-300x263.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-44.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-45-300x255.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-45.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-46-300x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-46.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-47.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-47.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-48-300x210.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-48.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-49-249x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-49.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-50-300x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-50.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-51-300x146.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-51.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/05/python-2.jpg",
"url": "https://www.probytes.net/python-development-company/"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/1.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/1.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/2.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/2.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/3.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/3.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/4-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/4-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/5-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/5-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/6-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/6-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/7-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/7-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/8-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/8-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/9-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/9-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/10-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/10-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/11-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/11-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/12-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/12-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/13-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/13-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/14-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/14-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/15-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/15-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/16.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/16.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/17.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/17.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/18.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/18.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/19.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/19.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/20.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/20.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/9-1.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/9-1.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/8-1.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/8-1.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/7.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/7.jpg"
},
{
"image": "https://s.w.org/images/core/emoji/12.0.0-1/svg/1f609.svg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/3-2.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/3-2.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/6-1.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/6-1.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/Imgur-8801b2-1.png",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/Imgur-8801b2-1.png"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/5-1.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/5-1.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/r_389776_tqMPa-1.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/r_389776_tqMPa-1.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/2-2.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/2-2.jpg"
},
{
"image": "https://www.probytes.net/wp-content/uploads/2018/01/4.jpg",
"url": "https://www.probytes.net/wp-content/uploads/2018/01/4.jpg"
}
]
}
Routes.swift file in Vapor
import Vapor
struct Welcome: Codable,Content {
let memes: [Meme]
}
struct Meme: Codable,Content {
let image: String
let url: String?
}
func routes(_ app: Application) throws {
var m = [Meme]()
var path = app.directory.publicDirectory
path.append("memes.json")
print(path)
guard let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else {return }
do
{
let memess = try JSONDecoder().decode(Welcome.self, from: data)
m = memess.memes
}
catch{
print(error.localizedDescription)
}
app.get { req in
return m
}
}
iOS App codable struct file
import Foundation
public struct Json4Swift_Base : Codable {
public let memes : [Memes]?
public enum CodingKeys: String, CodingKey {
case memes = "memes"
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
memes = try values.decodeIfPresent([Memes].self, forKey: .memes)
}
}
public struct Memes : Codable {
public let image : String?
public let url : String?
public enum CodingKeys: String, CodingKey {
case image = "image"
case url = "url"
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
image = try values.decodeIfPresent(String.self, forKey: .image)
url = try values.decodeIfPresent(String.self, forKey: .url)
}
}
NetworkService file
import Foundation
public class NetworkService
{
public static let sharedobj = NetworkService()
public let url = URL(string:"http://127.0.0.1:8080")
public let session = URLSession(configuration: .default)
public func getMemes(onSucces:#escaping([Memes],Error?)->Void)
{
let task = session.dataTask(with: url!) { (data, response, error) in
do
{
let items = try JSONDecoder().decode(Json4Swift_Base.self, from: data!)
onSucces(items.memes!,error)
}
catch
{
print(error.localizedDescription)
}
}
task.resume()
}
}
In your Vapor code you decode the file and then extract only the array to a property so assuming that this is what you encode and send to the client then you only receive the array.
Like this
"[
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-53-300x300.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-53.png"
},
{
"image": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-1-300x210.png",
"url": "https://www.testbytes.net/wp-content/uploads/2019/06/Untitled-1.png"
},
...
]"
So in your client you should decode the array only
let items = try JSONDecoder().decode([Memes].self, from: data!)
And you don't need init(from:) or a CodingKey enum here.

New Node should be added without changing the position of existing one using highchart

Highcharts.chart('container', {
chart: {
type: 'networkgraph',
...
I am using network-graph while adding or modifying node the existing node position is changing very much. If i want the changes without making the position change the what should I do.
code:
Highcharts.chart('container', {
chart: {
type: 'networkgraph',
backgroundColor: '#000000',
animation: 'false',
height: '500px',
//marginRight: '100px',
events: {
render() {
let chart = this;
//console.log(chart)
//render custom back button
chart.label = chart.renderer.label('Back', chart.plotWidth - 100, 50)
.css({
color: '#FFFFFF'
})
.attr({
fill: 'rgba(0, 0, 0, 0.75)',
padding: 8,
r: 5,
zIndex: 6,
})
.add()
//show it after initial load
chart.label.hide()
if (chart.forRender) {
chart.series[0].points.forEach((p, i) => {
p.graphic.element.onclick = function () {
chart.series[0].update({
//data: drilldownData[i][0].data,
//nodes: drilldownData[i][0].nodes
})
chart.forRender = false
chart.label.show();
}
})
}
chart.label.element.onclick = function () {
chart.forRender = true
chart.label.hide();
chart.series[0].update({
data: this.initialSeries.data,
nodes: this.initialSeries.nodes
})
}
}
}
},
title: {
text: '',
color: 'white'
},
plotOptions: {
networkgraph: {
layoutAlgorithm: {
linkLength: 30, // in pixels ,
enableSimulation: false ,
initialPositions: 'square',
// integration:"euler",
//attractiveForce: function (d, k) { return ( d -k); },
// repulsiveForce: function(){ return 1.5}
},
draggable: false,
dataLabels:{
enabled: true,
padding:12,
style:{
color:'#cbb6dd',
fontWeight: 'bold',
textOutline: '0px contrast'
}
}
}
},
series: [this.initialSeries]
}, function (chart) {
chart.forRender = true
});
json:
{
"data": [
{ "from": "OLO", "to": "CS003", "color": "#ffdd03" },
{ "from": "OLO", "to": "RGA", "color": "#ffffff" },
{ "from": "OLO", "to": "RGM", "color": "#ff0703" },
{ "from": "OLO", "to": "BDT", "color": "#ffffff" },
{ "from": "OLO", "to": "CW", "color": "#ffdd03" },
{ "from": "CS902", "to": "QTH", "color": "#ffffff" },
{ "from": "RGA", "to": "QTH", "color": "#ffdd03" },
{ "from": "RGA", "to": "CLC", "color": "#ff0703" },
{ "from": "RGM", "to": "SYE", "color": "#ffffff" },
{ "from": "SYE", "to": "CLC", "color": "#ff0703" },
{ "from": "BDT", "to": "CLC", "color": "#ffdd03" },
{ "from": "EHR", "to": "LVC", "color": "#ffffff" },
{ "from": "EHR", "to": "CLC", "color": "#ff0703" },
{ "from": "EHR", "to": "GWW", "color": "#ffffff" },
{ "from": "EHR", "to": "GWT1", "color": "#ff0703" },
{ "from": "EHR", "to": "TPZ", "color": "#ffdd03" },
{ "from": "EHR", "to": "NGC", "color": "#ffdd03" },
{ "from": "EHR", "to": "CS004", "color": "#ffffff" },
{ "from": "GWW", "to": "L/T1", "color": "#ff0703" },
{ "from": "CS004", "to": "MRT1", "color": "#ffdd03" }
],
"nodes": [
{
"id": "999 TXT",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "ASU",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "VRU",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "Internet",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "Dail IT",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "VMP",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "CS05",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "MSP",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "VO",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "OR",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "O2",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "Mobile",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "CW",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "TW",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "Resilient Network",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "Jet",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "ONLY X",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "TOAD",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "VUL",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "CRAWLEY",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "BT",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "Others",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "VOIP ITP",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "Old Exchanges",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "SDIN",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "PH/1",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "OLO",
"marker": {
"symbol": "url(assets/images/server.png)"
}
},
{
"id": "CS003",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "RGA",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "RGM",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "BDT",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "CS902",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "QTH",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "CLC",
"marker": {
"symbol": "url(assets/images/customer-service.png)"
}
},
{
"id": "SYE",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "EHR",
"marker": {
"symbol": "url(assets/images/teamwork.png)"
}
},
{
"id": "LVC",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "GWW",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "GWT1",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "TPZ",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "NGC",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "L/T1",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "CS004",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
},
{
"id": "MRT1",
"marker": {
"symbol": "url(assets/images/computer.png)"
}
}
],
"name": "Main Series",
"dataLabels": {
"draggable": "false",
"enabled": "true"
},
"link": {
"width": 3
}
}
So can you please let me know how we can achieve this.
You can zero maxIterations property and define custom postions for the new points in redraw event:
chart: {
...,
events: {
load: function() {
var chart = this;
setTimeout(function() {
chart.series[0].addPoint(['G', 'Z'], true);
}, 2000);
},
redraw: function() {
var newNode = this.series[0].nodes[7];
newNode.plotX = 100;
newNode.plotY = 100;
}
}
}
Live demo: https://jsfiddle.net/BlackLabel/t742a9kh/
API Refefence:
https://api.highcharts.com/highcharts/series.networkgraph.layoutAlgorithm.maxIterations
https://api.highcharts.com/highcharts/chart.events.redraw

How to parse particular JSON in Swift

To parse a JSON, as I found also on the web, I usually used this kind of code:
guard let results = receivedUserJSON["results"] as? [String: Any] else {
print("Error interpreting results")
return
}
This time I have a problem, because it seems to end in the else of this guard let. The JSON has the following structure:
{
"results": [{
"gender": "female",
"name": {
"title": "mrs",
"first": "silene",
"last": "almeida"
},
"location": {
"street": "2594 rua maranhão ",
"city": "pouso alegre",
"state": "distrito federal",
"postcode": 20447,
"coordinates": {
"latitude": "-70.0198",
"longitude": "123.6577"
},
"timezone": {
"offset": "+4:30",
"description": "Kabul"
}
},
"email": "silene.almeida#example.com",
"login": {
"uuid": "d06a46b3-1c00-42be-b8fc-d271bf901f7d",
"username": "silversnake251",
"password": "ventura",
"salt": "UcckU6RG",
"md5": "7c8c4129587c61da01ca7cf4f88353c5",
"sha1": "6cbf7ec377ff4ebad5a392ec487343bf613858ef",
"sha256": "8dedf3649fb833a1936b8885627b86c6cf02062eb74f727b2cbd674a30f73e75"
},
"dob": {
"date": "1969-07-13T00:58:26Z",
"age": 49
},
"registered": {
"date": "2003-09-28T09:44:56Z",
"age": 15
},
"phone": "(95) 0094-8716",
"cell": "(20) 1014-3529",
"id": {
"name": "",
"value": null
},
"picture": {
"large": "https://randomuser.me/api/portraits/women/66.jpg",
"medium": "https://randomuser.me/api/portraits/med/women/66.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/women/66.jpg"
},
"nat": "BR"
}],
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}
What should I do to properly parse this JSON? I would prefer not to go for the Codable solution because I don't need all of these values.
PS: I know the json is correct because I tried and printed it with:
if let JSONString = String(data: responseData, encoding: String.Encoding.utf8) {
print(JSONString)
}
results is an array
guard let results = receivedUserJSON["results"] as? [[String:Any]] else {
print("Error interpreting results")
return
}
I see no value for it to be an array as it contains 1 element so you may think to alter this json
current strucsture
{
"results": [{}],
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}
you may alter it to
{
"results": {},
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}

Searchkick Aggregations behavior

I am working with the Searchkick Gem and Elastic search and am trying to understand the aggregations behavior.
I have three facets (Aggregations): City, State and Company.
If I filter by any one of them, the counts of other two are reduced to reflect the total in the result set. But the selected facet comes back with all values. So say I had 100 items in the index, and I filtered by a Company that had 2 total items in the index, the City and State counts are updated to reflect no more than 2. But the Company count remains at 100.
Example (filtered to City=Atlanta)
{
"query": {
"function_score": {
"functions": [
{
"filter": {
"and": [
{
"term": {
"featured": true
}
}
]
},
"boost_factor": 1000
}
],
"query": {
"match_all": {}
},
"score_mode": "sum"
}
},
"size": 20,
"from": 0,
"post_filter": {
"bool": {
"filter": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
},
{
"term": {
"city": "Atlanta"
}
}
]
}
},
"aggs": {
"company": {
"filter": {
"bool": {
"must": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
},
{
"term": {
"city": "Atlanta"
}
}
]
}
},
"aggs": {
"company": {
"terms": {
"field": "company",
"size": 10
}
}
}
},
"city": {
"filter": {
"bool": {
"must": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
}
]
}
},
"aggs": {
"city": {
"terms": {
"field": "city",
"size": 10
}
}
}
},
"state": {
"filter": {
"bool": {
"must": [
{
"range": {
"expiration_date": {
"from": "2016-08-18T23:07:15.670-04:00",
"include_lower": true
}
}
},
{
"range": {
"created_at": {
"to": "2016-08-18T23:07:15.670-04:00",
"include_upper": true
}
}
},
{
"term": {
"published": true
}
},
{
"term": {
"tenant_id": 4
}
},
{
"term": {
"city": "Atlanta"
}
}
]
}
},
"aggs": {
"state": {
"terms": {
"field": "state",
"size": 10
}
}
}
}
},
"fields": []
}
Result (2 result returned, but 58 City Aggregations come back). Note Company and City return correct # of Aggregations:
{
"took": 114,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1,
"hits": [
{
"_index": "jobs_development_20160818140128648",
"_type": "job",
"_id": "457134",
"_score": 1
},
{
"_index": "jobs_development_20160818140128648",
"_type": "job",
"_id": "457137",
"_score": 1
}
]
},
"aggregations": {
"city": {
"doc_count": 58,
"city": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 19,
"buckets": [
{
"key": "Los Angeles",
"doc_count": 8
},
{
"key": "London",
"doc_count": 7
},
{
"key": "New York",
"doc_count": 7
},
{
"key": "Burbank",
"doc_count": 5
},
{
"key": "Pasig",
"doc_count": 3
},
{
"key": "Atlanta",
"doc_count": 2
},
{
"key": "Chicago",
"doc_count": 2
},
{
"key": "Culver City",
"doc_count": 2
},
{
"key": "London Borough of Hackney",
"doc_count": 2
},
{
"key": "Birmingham",
"doc_count": 1
}
]
}
},
"company": {
"doc_count": 2,
"company": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Second Story",
"doc_count": 2
}
]
}
},
"state": {
"doc_count": 2,
"state": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "Georgia",
"doc_count": 2
}
]
}
}
}
}
What am I missing? Is this correct behavior?

Count ocurrence of two fields

I have this kind of documents in a MongoDb collection:
[
{
"_id": {
"id": 892,
"answer": "C",
"level": "regular"
},
"total": 4
},
{
"_id": {
"id": 891,
"answer": "Regular",
"level": "neutral"
},
"total": 3
},
{
"_id": {
"id": 892,
"answer": "B",
"level": "regular"
},
"total": 3
},
{
"_id": {
"id": 891,
"answer": "Ótimo",
"level": "positive"
},
"total": 5
},
{
"_id": {
"id": 892,
"answer": "E",
"level": "regular"
},
"total": 3
},
{
"_id": {
"id": 891,
"answer": "Bom",
"level": "positive"
},
"total": 1
},
{
"_id": {
"id": 891,
"answer": "Ruim",
"level": "negative"
},
"total": 2
},
{
"_id": {
"id": 892,
"answer": "D",
"level": "regular"
},
"total": 3
},
{
"_id": {
"id": 891,
"answer": "Péssimo",
"level": "negative"
},
"total": 3
},
{
"_id": {
"id": 892,
"answer": "F",
"level": "regular"
},
"total": 1
}
]
I'm trying to count answer and level ocurrences using MongoDb aggregation pipiline. I'm expecting some output like this:
[
{
"id": 891,
"answers": [
{
"answer": "Ótimo",
"count": 5
},
{
"answer": "Bom",
"count": 1
},
{
"answer": "Regular",
"count": 3
},
{
"answer": "Ruim",
"count": 2
},
{
"answer": "Péssimo",
"count": 3
}
],
"levels": [
{
"level": "positive",
"count": 6
},
{
"level": "neutral",
"count": 3
},
{
"level": "negative",
"count": 5
}
],
"total": 14
},
{
"id": 892,
"answers": [
{
"answer": "B",
"count": 3
},
{
"answer": "C",
"count": 4
},
{
"answer": "D",
"count": 3
},
{
"answer": "E",
"count": 3
},
{
"answer": "F",
"count": 1
},
],
"levels": [
{
"level": "regular",
"count": 14
}
],
"total": 14
}
]
How could I achieve the desired output using MongoDb aggregation pipeline?
EDIT: Actually I'm already using $group to achieve something like that, but I guess only one $group step on the pipeline will not achieve the desired output. Here is my current $group step:
{
$group: {
_id: {
id: "$_id.id"
},
answers: {
$push: {
answer: "$_id.answer",
count: "$count"
}
},
levels: {
$push: {
level: "$_id.level",
count: "$count"
}
},
total: { $sum: "$count" }
}
}
Here is the output I have so far:
[
{
"_id": {
"id": 892
},
"answers": [
{
"answer": "F",
"count": 1
},
{
"answer": "D",
"count": 3
},
{
"answer": "E",
"count": 3
},
{
"answer": "C",
"count": 4
},
{
"answer": "B",
"count": 3
}
],
"levels": [
{
"level": "regular"
},
{
"level": "regular"
},
{
"level": "regular"
},
{
"level": "regular"
},
{
"level": "regular"
}
]
},
{
"_id": {
"id": 891
},
"answers": [
{
"answer": "Ruim",
"count": 2
},
{
"answer": "Péssimo",
"count": 3
},
{
"answer": "Bom",
"count": 1
},
{
"answer": "Regular",
"count": 3
},
{
"answer": "Ótimo",
"count": 5
}
],
"levels": [
{
"level": "negative"
},
{
"level": "negative"
},
{
"level": "positive"
},
{
"level": "neutral"
},
{
"level": "positive"
}
]
}
]
The following aggregation pipeline:
{ $group: {
_id: { id: "$_id.id" },
answers: {
$push: {
answer: "$_id.answer",
level: "$_id.level",
count: "$total"
}
},
levels: {
$push: {
level: "$_id.level",
count: "$total"
}
}
}},
{ $unwind: $levels },
{ $group: {
_id: {
id: "$_id.id",
level: "$levels.level"
},
answers: { $addToSet: "$answers" },
total: { $sum: "$levels.count" }
}},
{ $group: {
_id: { id: "$_id.id" },
answers: { $addToSet: "$answers" },
levels: {
$push: {
level: "$_id.level",
count: "$total"
}
},
total: { $sum: "$total" }
}},
{ $project: {
_id: 0,
id: "$_id.id",
answers: 1,
levels: 1,
total: 1
}}
Will produce the desired output.

Resources