in a F# class, how can I use a value in another value at initialization time? - constructor

I'm trying to replicate C# code that has three dictionaries:
Dictionary<string, float> DictionaryA = new Dictionary<string, float>();
Dictionary<string, float> DictionaryB = new Dictionary<string, float>();
Dictionary<MyEnum, Dictionary<string, float>> Dictionaries
= new Dictionary<MyEnum, Dictionary<string, float>>();
and, in the constructor:
Dictionaries[MyEnum.A] = DictionaryA
Dictionaries[MyEnum.B] = DictionaryB
so, in F#, I do this:
member val private DictionaryA = new Dictionary<string, float>()
member val private DictionaryB = new Dictionary<string, float>()
but then, in the constructor, it looks like 'this' is not available:
do
this.Dictionaries.Add(MyEnum.A, this.DictionaryA) |> ignore
this.Dictionaries.Add(MyEnum.B, this.DictionaryB) |> ignore
will not work.
how can I initialize that third dictionary?

It seems you won't mutate the fields itself after constructing the object, so
open System.Collections.Generic
type Foo() =
let a = new Dictionary<string, float>()
let b = new Dictionary<string, float>()
let t = new Dictionary<int, Dictionary<string, float>>()
do
t.Add(1, a)
t.Add(2, b)

Related

.NET Core Configuration Serialization

Is there a way to serialize an object so that it could then be rehydrated by .Net Core Configuration Binder?
Basically, I'd like to get this Test to pass:
[Test]
public void Can_Serialize_And_Rehydrate()
{
var foo = new Foo{ Prop1 = 42; Prop2 = "Test" }
Dictionary<string, string> serialized = Serialize(Foo);
var deserializedFoo = new Foo();
new ConfigurationBuilder()
.AddInMemoryCollection(serialized)
.Build()
.Bind(deserializedFoo);
Assert.AreEqual(deserializedFoo.Prop1, 42);
Assert.AreEqual(deserializedFoo.Prop2, "Test");
}
Is there a Serializer out-of-the-box, or am I'm going to need to write my own Serialize() method?
AddInMemoryCollection's signature is like below, so why are you trying to serialize your dictionary here? You could just use it as it is.
public static IConfigurationBuilder AddInMemoryCollection(
this IConfigurationBuilder configurationBuilder,
IEnumerable<KeyValuePair<string, string>> initialData)
If you like to know more about how to test your custom configurations, I would suggest to look here:
https://github.com/aspnet/Configuration/blob/1.0.0/test/Microsoft.Extensions.Configuration.Binder.Test/ConfigurationBinderTests.cs
I was able to get this working by "hijacking" a JsonConfigurationProvider and plugging serialized Json directly into it. Not sure if this is the best way, but it does work:
public class ConfigurationSerializer
{
private class CustomJsonProvider : JsonConfigurationProvider
{
public CustomJsonProvider() : base(new JsonConfigurationSource())
{
}
public IDictionary<string, string> GetData(Stream s)
{
Load(s);
// Return the Configuration Dictionary
return Data;
}
}
public Dictionary<string, string> Serialize(object o)
{
var serialized =
JsonConvert.SerializeObject(
o,
new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore});
using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(serialized)))
{
var jsonProvider = new CustomJsonProvider();
return jsonProvider
.GetData(ms)
.ToDictionary(key => key.Key, value => value.Value);
}
}
}

How to ignore duplicate keys while parsing json using gson?

I am getting a a duplicate key exception while parsing JSON response containing timestamps as keys using GSON. It gives the following error:
com.google.gson.JsonSyntaxException: duplicate key: 1463048935
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:186)
at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:141)
How do I make it ignore the duplicate entries, and just parse it to a map with any one from the duplicate entries?
Hackerman solution, tested and working using GSON v2.8.5, but use at your own risk! Whenever you update GSON to a new version, make sure to check whether this is still working!
Basically, you can use the fact that the generic ObjectTypeAdapter ignores duplicates as seen here:
Looks like MapTypeAdapterFactory checks for duplicate
V replaced = map.put(key, value);
if (replaced != null) {
throw new JsonSyntaxException("duplicate key: " + key);
}
however ObjectTypeAdapter does not
case BEGIN_OBJECT:
Map<String, Object> map = new LinkedTreeMap<String, Object>();
in.beginObject();
while (in.hasNext()) {
map.put(in.nextName(), read(in));
}
in.endObject();
return map;
What you can do now is trying to deserialize using fromJson as usual, but catch the "duplicate key" exception, deserialize as a generic Object, which will ignore duplicates, serialize it again, which will result in a JSON string without duplicate keys, and finally deserialize using the correct type it's actually meant to be.
Here is a Kotlin code example:
fun <T> String.deserialize(gson: Gson, typeToken: TypeToken<T>): T {
val type = typeToken.type
return try {
gson.fromJson<T>(this, type)
} catch (e: JsonSyntaxException) {
if (e.message?.contains("duplicate key") == true) {
gson.toJson(deserialize(gson, object : TypeToken<Any>() {})).deserialize(gson, typeToken)
} else {
throw e
}
}
}
Obviously, this adds (potentially heavy) overhead by requiring 2 deserializations and an additional serialization, but currently I don't see any other way to do this. If Google decides to add an option for a built-in way to ignore duplicates, as suggested here, better switch to that.
I couldn't use Kotlin (as answered before), so I adjusted it to Java
It could be achieved by registering the type adder:
#Test
void testDuplicatesIgnored() {
String json = "{\"key\": \"value\", \"key\": \"value2\"}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(Map.class, new JsonDeserializer<Map<String, Object>>() {
#Override
public Map<String, Object> deserialize(JsonElement json1, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Gson().fromJson(json1, typeOfT);
}
})
.create();
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
Map<String, Object> map = gson.fromJson(json, mapType);
System.out.println("map = " + map); // map = {key=value2}
assertThat(map).hasSize(1);
assertThat(map.get("key")).isEqualTo("value2");
}
This way all the mappings to Map.class will go through your deserializer code
Yea, looks like a dirty hack, but it works
Another way is to register type adder for your custom type to make the deserializer being called only where you need it:
#Test
void testDuplicatesIgnored() {
String json = "{\"key\": \"value\", \"key\": \"value2\"}";
Gson gson = new GsonBuilder()
.registerTypeAdapter(MapIgnoringDuplicatesContainer.class, new JsonDeserializer<MapIgnoringDuplicatesContainer>() {
#Override
public MapIgnoringDuplicatesContainer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
return new MapIgnoringDuplicatesContainer(new Gson().fromJson(json, mapType));
}
})
.create();
Map<String, Object> map = gson.fromJson(json, MapIgnoringDuplicatesContainer.class).getMap();
System.out.println("map = " + map); // map = {key=value2}
assertThat(map).hasSize(1);
assertThat(map.get("key")).isEqualTo("value2");
}
private class MapIgnoringDuplicatesContainer {
private Map<String, Object> map;
public MapIgnoringDuplicatesContainer(Map<String, Object> map) {
this.map = map;
}
public Map<String, Object> getMap() {
return map;
}
}
```

From Java Object to JSON String using javax.json

Is it possible to create a JSON string from List<MyClass> using Java EE 7's javax.json library without iterating over the List<MyClass>?
This is what I do now and the performance is unacceptable with 2000 iteration. Any suggestion?
List<MyClass> items = MyDB.getAllItems();
JsonObjectBuilder builder = Json.createObjectBuilder();
builder.add("success", true);
JsonArrayBuilder childrenArrayBuilder = Json.createArrayBuilder();
for (MyClass item : items) {
childrenArrayBuilder.add(
Json.createObjectBuilder()
.add("id", getTreeNodeId(item) + "-" + (idSplit[1]))
.add("nodeStatus", b)
.add("text", item.getName())
.add("leaf", false));
}
You are creating a lot of objects in your loops which is probably not necessary. With GSON and as far as MyClass contains only what you need I will do something like:
List<MyClass> list = MyDB.getAllItems();
GSON gson = new GSON();
String myString = gson.toJson(list);

How to recursively deserialize dynamic JSON into .NET objects using Json.NET on windows phone [duplicate]

I need to deserialize a complex JSON blob into standard .NET containers for use in code that is not aware of JSON. It expects things to be in standard .NET types, specifically Dictionary<string, object> or List<object> where "object" can be primitive or recurse (Dictionary or List).
I cannot use a static type to map the results and JObject/JToken don't fit. Ideally, there would be some way (via Contracts perhaps?) to convert raw JSON into basic .NET containers.
I've search all over for any way to coax the JSON.NET deserializer into creating these simple types when it encounters "{}" or "[]" but with little success.
Any help appreciated!
If you just want a generic method that can handle any arbitrary JSON and convert it into a nested structure of regular .NET types (primitives, Lists and Dictionaries), you can use JSON.Net's LINQ-to-JSON API to do it:
using System.Linq;
using Newtonsoft.Json.Linq;
public static class JsonHelper
{
public static object Deserialize(string json)
{
return ToObject(JToken.Parse(json));
}
public static object ToObject(JToken token)
{
switch (token.Type)
{
case JTokenType.Object:
return token.Children<JProperty>()
.ToDictionary(prop => prop.Name,
prop => ToObject(prop.Value));
case JTokenType.Array:
return token.Select(ToObject).ToList();
default:
return ((JValue)token).Value;
}
}
}
You can call the method as shown below. obj will either contain a Dictionary<string, object>, List<object>, or primitive depending on what JSON you started with.
object obj = JsonHelper.Deserialize(jsonString);
One way to deserialize a json string recursively into dictionaries and lists with JSON.NET is to create a custom json converter class that derives from the JsonConverter abstract class provided by JSON.NET.
It is in your derived JsonConverter where you put the implementation of how an object should be written to and from json.
You can use your custom JsonConverter like this:
var o = JsonConvert.DeserializeObject<IDictionary<string, object>>(json, new DictionaryConverter());
Here is a custom JsonConverter I have used with success in the past to achieve the same goals as you outline in your question:
public class DictionaryConverter : JsonConverter {
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { this.WriteValue(writer, value); }
private void WriteValue(JsonWriter writer, object value) {
var t = JToken.FromObject(value);
switch (t.Type) {
case JTokenType.Object:
this.WriteObject(writer, value);
break;
case JTokenType.Array:
this.WriteArray(writer, value);
break;
default:
writer.WriteValue(value);
break;
}
}
private void WriteObject(JsonWriter writer, object value) {
writer.WriteStartObject();
var obj = value as IDictionary<string, object>;
foreach (var kvp in obj) {
writer.WritePropertyName(kvp.Key);
this.WriteValue(writer, kvp.Value);
}
writer.WriteEndObject();
}
private void WriteArray(JsonWriter writer, object value) {
writer.WriteStartArray();
var array = value as IEnumerable<object>;
foreach (var o in array) {
this.WriteValue(writer, o);
}
writer.WriteEndArray();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
return ReadValue(reader);
}
private object ReadValue(JsonReader reader) {
while (reader.TokenType == JsonToken.Comment) {
if (!reader.Read()) throw new JsonSerializationException("Unexpected Token when converting IDictionary<string, object>");
}
switch (reader.TokenType) {
case JsonToken.StartObject:
return ReadObject(reader);
case JsonToken.StartArray:
return this.ReadArray(reader);
case JsonToken.Integer:
case JsonToken.Float:
case JsonToken.String:
case JsonToken.Boolean:
case JsonToken.Undefined:
case JsonToken.Null:
case JsonToken.Date:
case JsonToken.Bytes:
return reader.Value;
default:
throw new JsonSerializationException
(string.Format("Unexpected token when converting IDictionary<string, object>: {0}", reader.TokenType));
}
}
private object ReadArray(JsonReader reader) {
IList<object> list = new List<object>();
while (reader.Read()) {
switch (reader.TokenType) {
case JsonToken.Comment:
break;
default:
var v = ReadValue(reader);
list.Add(v);
break;
case JsonToken.EndArray:
return list;
}
}
throw new JsonSerializationException("Unexpected end when reading IDictionary<string, object>");
}
private object ReadObject(JsonReader reader) {
var obj = new Dictionary<string, object>();
while (reader.Read()) {
switch (reader.TokenType) {
case JsonToken.PropertyName:
var propertyName = reader.Value.ToString();
if (!reader.Read()) {
throw new JsonSerializationException("Unexpected end when reading IDictionary<string, object>");
}
var v = ReadValue(reader);
obj[propertyName] = v;
break;
case JsonToken.Comment:
break;
case JsonToken.EndObject:
return obj;
}
}
throw new JsonSerializationException("Unexpected end when reading IDictionary<string, object>");
}
public override bool CanConvert(Type objectType) { return typeof(IDictionary<string, object>).IsAssignableFrom(objectType); }
}
Here is the equivalent in f#:
type IDictionaryConverter() =
inherit JsonConverter()
let rec writeValue (writer: JsonWriter) (value: obj) =
let t = JToken.FromObject(value)
match t.Type with
| JTokenType.Object -> writeObject writer value
| JTokenType.Array -> writeArray writer value
| _ -> writer.WriteValue value
and writeObject (writer: JsonWriter) (value: obj) =
writer.WriteStartObject ()
let obj = value :?> IDictionary<string, obj>
for kvp in obj do
writer.WritePropertyName kvp.Key
writeValue writer kvp.Value
writer.WriteEndObject ()
and writeArray (writer: JsonWriter) (value: obj) =
writer.WriteStartArray ()
let array = value :?> IEnumerable<obj>
for o in array do
writeValue writer o
writer.WriteEndArray ()
let rec readValue (reader: JsonReader) =
while reader.TokenType = JsonToken.Comment do
if reader.Read () |> not then raise (JsonSerializationException("Unexpected token when reading object"))
match reader.TokenType with
| JsonToken.Integer
| JsonToken.Float
| JsonToken.String
| JsonToken.Boolean
| JsonToken.Undefined
| JsonToken.Null
| JsonToken.Date
| JsonToken.Bytes -> reader.Value
| JsonToken.StartObject -> readObject reader Map.empty
| JsonToken.StartArray -> readArray reader []
| _ -> raise (JsonSerializationException(sprintf "Unexpected token when reading object: %O" reader.TokenType))
and readObject (reader: JsonReader) (obj: Map<string, obj>) =
match reader.Read() with
| false -> raise (JsonSerializationException("Unexpected end when reading object"))
| _ -> reader.TokenType |> function
| JsonToken.Comment -> readObject reader obj
| JsonToken.PropertyName ->
let propertyName = reader.Value.ToString ()
if reader.Read() |> not then raise (JsonSerializationException("Unexpected end when reading object"))
let value = readValue reader
readObject reader (obj.Add(propertyName, value))
| JsonToken.EndObject -> box obj
| _ -> raise (JsonSerializationException(sprintf "Unexpected token when reading object: %O" reader.TokenType))
and readArray (reader: JsonReader) (collection: obj list) =
match reader.Read() with
| false -> raise (JsonSerializationException("Unexpected end when reading array"))
| _ -> reader.TokenType |> function
| JsonToken.Comment -> readArray reader collection
| JsonToken.EndArray -> box collection
| _ -> collection # [readValue reader] |> readArray reader
override __.CanConvert t = (typeof<IDictionary<string, obj>>).IsAssignableFrom t
override __.WriteJson (writer:JsonWriter, value: obj, _:JsonSerializer) = writeValue writer value
override __.ReadJson (reader:JsonReader, _: Type, _:obj, _:JsonSerializer) = readValue reader
I love AutoMapper and seem to think it solves many problems... like this one...
why not just let the JSON.NET convert the thing into whatever it wants to... and use AutoMapper to map it into the object you really want.
Unless performance is paramount this extra step should be worth it for the reduction in complexity and the ability to use the serializer you want.
You can have full control over the serialization of a type by using a custom JsonConverter. Documentation at http://james.newtonking.com/projects/json/help/html/T_Newtonsoft_Json_JsonConverter.htm .
Also, according to this blog post you need to use JArray for a List, and JObject for a dictionary.
You cannot do what I was asking. At least not as far as I can tell after MUCH research. I had to edit the source of Json.NET.

Mongo C# driver dictionary serizalization

I'm trying to configure document serialization in my app. Problem is - seriializing object have dictionary with a custom data and some times keys in dictionary looks like "Test.0" or "Test_0".
If dictionary have "Test_0" key - dictionary will serialized as BsonDocument.
If dictionary have "Test.0" key - result will be BsonArray as I understand.
Is it expected behavior?
I've tried to find some documenttation about - nothing found.
Couple of test ti illustrate the problem (added Newtonsoft.Json just to compare behavior)
[TestFixture]
public class BsonDocumentWrapperTests
{
[Test]
public void Test_WithoutDot()
{
var value = new Dictionary<string, object>();
value.Add("test0", "test0");
var result = BsonDocumentWrapper.Create(value);
Assert.AreEqual("{ \"test0\" : \"test0\" }", result.ToJson());
}
[Test]
public void Test_WithDot()
{
var value = new Dictionary<string, object>();
value.Add("test.0", "test0");
var result = BsonDocumentWrapper.Create(value);
Assert.AreEqual("{ \"test.0\" : \"test0\" }", result.ToJson());
}
[Test]
public void Test_WithDot_JsonNet()
{
var value = new Dictionary<string, object>();
value.Add("test.0", "test0");
var result = JsonConvert.SerializeObject(value);
Assert.AreEqual("{\"test.0\":\"test0\"}", result);
}
}