I noticed some strange output from Newtonsoft.Json today, I'm not sure if it is an interaction with F# types or something that can occur in C# as well, so I've tagged both. I have a list of the following record being serialized:
type SplitTracker =
{
[<JsonIgnore>]
split : SplitDefinition
mutable start : duration
mutable ``end`` : duration
mutable lapCount : int
mutable duration : duration Option
}
I serialize it with JsonConvert.SerializeObject and I get the following odd output:
"splits": [
{
"start#": "0.00",
"end#": "0.00",
"lapCount#": 0,
"duration#": null,
"start": "0.00",
"end": "0.00",
"lapCount": 0,
"duration": null
},
{
"start#": "0.00",
"end#": "0.00",
"lapCount#": 0,
"duration#": null,
"start": "0.00",
"end": "0.00",
"lapCount": 0,
"duration": null
}
Anyone know why that might be happening? The data is correct, the duplication of fields with the "#" symbol is the issue.
The way you have defined your record is the culprit here. Record fields are exposed as properties - but you are using mutable properties. F# will turn that into a class that has fields for each of your mutables (the name is the property name, prefixed with #), and properties that read out those.
Json will now attempt to serialize all fields and all properties - hence you get the duplication.
Try it out in F# interactive:
type SplitTracker =
{
mutable start : float
}
let t = typeof<SplitTracker>
let fields1 = t.GetFields() // This will give you a field '#start'
let props1 = t.GetProperties() // This will give you a property 'start'
Contrast that with what you get when using a plain record:
type SplitTracker2 =
{
start : float
}
let t2 = typeof<SplitTracker2>
let fields2 = t2.GetFields() // You will not see any fields
let props2 = t2.GetProperties() // There is a single property 'start'
This should serialize correctly. Apart from that, it makes your code more idiomatic.
Related
Cosider an API which reply is always of this structure:
{
"pagination": {
"limit": int,
"offset": int,
"count": int,
"total": int
},
"data": [
{...some obj...}
]
}
So payloads differ only in structure of data objects.
Ideally I'd like to tell F# that all types built from samples have some common part - pagination info, so I can have one generic method which reads all pages.
Is it possible, or do I have to extract pagination object and data array separately with two type providers? I see the benefit of having one provider per response body as it supports reading data from the stream.
I would define two different provided types, one for parsing the pagination data and one for parsing the actual data, i.e. something like this:
type Pagination = JsonProvider<"""{
"pagination": { "limit": 1, "offset": 2,
"count": 3, "total": 4 }
}""">
type OneDataType = JsonProvider<"""{
"data": [ {"a": 1} ]
}""">
If you want to avoid parsing the same JSON file twice (e.g. by calling Pagination.Parse and OneDataType.Parse on the same string), you can actually just parse the data once and then pass the parsed JsonValue to the other type:
let odt = OneDataType.Load("/some/file")
let pg = Pagination.Root(odt.JsonValue)
pg.Pagination.Count
If you wanted to do this with a single provided type, then you could define multiple different fields for the multiple different types of data - but you'd have to name those differently. You'd then need to do some fiddling to read the data correctly. I would not do this, because I find it confusing, but it would look something like this:
type AnyData = JsonProvider<"""{
"pagination": { "limit": 1, "offset": 2,
"count": 3, "total": 4 },
"data": [],
"one_data_type": [ {"a":1} ],
"another_data_type": [ {"b":"xx" }]
}""">
let a = AnyData.Load("/some/file")
// Access pagination data
a.Pagination
// Access data as if it was OneDataType
let oneData = [| for d in a.Data ->
AnyData.OneDataType(d.JsonValue) |]
This piece of code:
open FSharp.Data
type ColorProvider = JsonProvider<"""
[
{
"color": "Red",
"code": 15
},
{
"color": "Green"
}
]
""", SampleIsList=true>
let value = ColorProvider.Root(color = "Blue", code = None)
printf "%A" value
Produces this JSON:
{
"color": "Blue",
"code": null
}
I'm passing the json to an external service which doesn't handle nulls. Either it must be an integer or the whole property must be missing.
Is there a way to hide the code property when it's null?
You are using FSharp's object printing feature for generating JSON. It produces JSON-like output, but in my opinion it is not meant for that task. I'd suggest using a JSON library for that, personally I like using https://github.com/Microsoft/fsharplu/wiki/fsharplu.json for that task, as it produces beautiful JSON for Discriminate Unions.
I am struggling a little bit to use DDMathParser framework for expression requirement I have. I have JSON of fields & based on expressions certain fields can be set required, hidden or set the value of it.
Expressions in required tag in sample JSON are not fixed & so not getting how to achieve dynamic approach for expression.
[
{
"name": "firstName",
"value": "Ameer",
**"required": true**
},
{
"name": "lastName",
"value": "Shaikh",
**"required": "$firstName != ‘’"**
},
{
"name": "designation",
"value": "",
**"required": "$firstName == ‘Ameer’ && $lastName == ‘Shaikh’"**
},
{
"name": "location",
"value": "",
**"hidden": false**
}
]
Actually, expression in JSON contains $ to represent one of the
dictionary objects value from JSON. Wherein framework internally
treats it as a local variable.
These expressions may have different combinations as well. There may be several expression apart from "required" parameters. I need to run all relevant expressions for any change in value in UI.
EDIT
let expression = "$firstName == ‘Ameer’ && $lastName == ‘Shaikh’"
let dict = ["firstName": "Amir", "lastName": ""]
let e = try! Expression(string: expression)
let result = try! Evaluator.default.evaluate(e, substitutions: dict)
Though it should parse a correct value from JSON, I have hard coded substitutions string to at least get a breakthrough. Here, substitutions expect String & Double & give error as "Cannot convert a value of type [String: String] to expected arg type substitutions (Dcitionary).
Is there any way to pass String: String substitutions?
DDMathParser is not built to do string evaluations. It's technically possible to make it work, but it's a bit beyond the scope of the framework.
For this situation, I think you'd be better off using NSPredicate, which does allow string comparisons and variable substitutions.
I have a dictionary like <String,Loto> and Loto is object like below;
Loto:
{
"success": true,
"data": {
"oid": "64kbbqi8dbxygb00",
"hafta": 961,
"buyukIkramiyeKazananIl": "",
"cekilisTarihi": "11/04/2015",
"cekilisTuru": "SAYISAL_LOTO",
"rakamlar": "03#02#48#16#15#08",
"rakamlarNumaraSirasi": "02 - 03 - 08 - 15 - 16 - 48",
"devretti": false,
"devirSayisi": 0,
"bilenKisiler": [
{
"oid": "64kbbxi8dbxyg403",
"kisiBasinaDusenIkramiye": 7.35,
"kisiSayisi": 185712,
"tur": "$3_BILEN"
},
{
"oid": "64kbbxi8dbxyg402",
"kisiBasinaDusenIkramiye": 53.05,
"kisiSayisi": 9146,
"tur": "$4_BILEN"
},
{
"oid": "64kbbxi8dbxyg401",
"kisiBasinaDusenIkramiye": 4532.2,
"kisiSayisi": 142,
"tur": "$5_BILEN"
},
{
"oid": "64kbbxi8dbxyg400",
"kisiBasinaDusenIkramiye": 1528438.75,
"kisiSayisi": 1,
"tur": "$6_BILEN"
}
],
"buyukIkrKazananIlIlceler": [
{
"il": "10",
"ilView": "BALIKESÄ°R",
"ilce": "01001",
"ilceView": "AYVALIK"
}
],
"kibrisHasilati": 51127,
"devirTutari": 0.09,
"kolonSayisi": 10537872,
"kdv": 1599672.97,
"toplamHasilat": 10537872,
"hasilat": 8938199.03,
"sov": 893819.9,
"ikramiyeEH": 8044379.129999999,
"buyukIkramiye": 1528432.03,
"haftayaDevredenTutar": 0
}
}
So my dictionary like <"11042015",Loto> and i want to sort this dictionary by "hafta" property of loto object.
How can i do this? Please help me!
If Loto is an object with a hafta property, you can sort your dictionary by passing it into the sorted function, along with a closure that tells it how to order the entries:
sorted(dict) { $0.1.hafta < $1.1.hafta }
($0.1 and $1.1 because dictionaries present as a sequence of key/value pairs - you want to sort by a property of the value i.e. tuple entry 1)
Note, this will give you back a sorted array, of type [(String,Loto)] pairs, rather than a dictionary (as Swift dictionaries are unordered).
(if Loto is not really an object but rather another dictionary, you might need to do {$0.1["hafta"] < $1.1["hafta"]} - it really depends on how you’re holding your data - the good news is you don’t need to worry about optionals, since they can be compared with <)
If you don’t need the keys, just sort the values:
sorted(dict.values) { $0.hafta < $1.hafta }
which will give you back a sorted array of type [Loto]
Dictionaries are not ordered. What you need is to convert it to array, and then sort it. So something like this.
let lotoArray = lotoDictionary.allObjects as [Loto]
lotoArray.sort { $0.hafta < $1.hafta }
You can't. The keys of a dictionary are not sorted. You can get an array of the values and sort that array.
I have an NSDictionary like this :
{
"name": "Hayagreeva", //String
"age": 3, //Number
"subjects": { //Array
"rhymes": { //Dictionary 1
"test1": 10,
"test2": 20,
"test3": 30
},
"games": { //Dictionary 2
"test1": 40,
"test2": 50,
"test3": 60
},
"crayoning": { //Dictionary 3
"test1": 70,
"test2": 80,
"test3": 90
}
},
"date": "2008-02-16T10:06:00Z" //Date
}
Now I need to replace the value for key subjects > games > test1 from 40 to value 60. I know the lenghty process of taking another dictionary objects and create a similar dictionary but that process is time consuming and may be a wrong strategy. I have searched many questions also here but all of them have the way to remove the key for the first level dictionary (like removing key "subjects" in the above code). So I want to know if there is an easy and efficient way to update the value for key in an inner level as I need to execute this process at many situations in my current project. Thanks in advance.
Edit : I also tried something like
[dict removeValueForKey:#"subjects.games.test1"];
But that doesn't work. I hope that this line helps you to understand the desired functionality by me.
If that structure is set in stone and will not change then it's as simple as:
NSDictionary *topLevelDict = ...;
topLevelDict[#"subjects"][#"games"] = #{
#"test1" : #(60),
#"test2" : #(50),
#"test3" : #(60)
};
Dictionaries of dictionaries are a pain to manipulate and it's always better to create a custom model object that allows easier manipulation, validation with the additional benefit of domain-specific functionality like serialization etc.