Can Json.NET deserialize to IEnumerable<KeyValuePair<string,string>>? - json.net

Can Json.NET deserialize to this?
public class C
{
[JsonConstructor]
public C(IEnumerable<KeyValuePair<string,string>> values)
...
}

Just deserialize to a Dictionary<string, string> and call ToList() on it.
var myJson = #"{""key1"": ""value1"", ""key2"": ""value2""}";
IEnumerable<KeyValuePair<string, string>> myEnumerable;
myEnumerable = JsonConvert.DeserializeObject<Dictionary<string, string>>(myJson).ToList();

Related

SmartFormat best way to serialize a dictionary

I have a data object with an dictionary.
Now I want to serialize this dictionary to a json string.
Is it possible to do this inside the template?
public string GenerateTest()
{
Dictionary<string, object> dataDictionary = new Dictionary<string, object>();
dataDictionary.Add("Testdata1", "Value1");
dataDictionary.Add("Testdata2", "Value2");
string result = Smart.Format(CultureInfo.InvariantCulture, "{data.someFormattertoGetAnJsonString}", new {data= dataDictionary });
Console.WriteLine(result);
return result;
}
Sure you could do that, but not in a generic way. SmartFormat is a formatter, rather than a serializer. So in general, SmartFormat is best in filling a text template with data, like it is required with mail merge.
In your case, you'll be better off using serializers like System.Text.Json or Newtonsoft.Json.
For the latter, here is an example how simple this works: https://www.newtonsoft.com/json/help/html/serializedictionary.htm
I have attached my solution. You have to register the ToJSONFormatter with the AddExtensions Method. After that you can call it like this: {MyVariable:ToJSON()}
Smart.Default.AddExtensions(new ToJSONFormatter());
public class ToJSONFormatter : IFormatter
{
public string Name { get; set; } = "ToJSON";
public bool CanAutoDetect { get; set; } = false;
private JsonSerializerSettings JsonSerializerSettings = new JsonSerializerSettings { DateFormatString = "yyyy-MM-ddTHH:mm:ss" };
//{Data:ToJSON()}
public bool TryEvaluateFormat(IFormattingInfo formattingInfo)
{
formattingInfo.Write(JsonConvert.SerializeObject(formattingInfo.CurrentValue));
return true;
}
}

DynamoDBMappingException on mapping Map<String, Object>attribute

I am trying to save the instance of the class bellow into dynamdb but getting
DynamoDBMappingException: not supported; requires #DynamoDBTyped or #DynamoDBTypeConverted exception.
#DynamoDBTable(tableName = "FulfillmentOrders")
public class FulfillmentOrder {
#DynamoDBHashKey
private String orderId;
#DynamoDBAttribute
#DynamoDBTyped(value = DynamoDBMapperFieldModel.DynamoDBAttributeType.M)
private Map<String, Object> body;
.......
}
It fails during map conversion, seems the problem is in Object generic type.
could someone help, where is the problem here or maybe SDK doesn't support such kind of conversion ?
Thanks!
DynamoDB won't know how to convert the objects in the Map<,>, you'll have to create a custom type converter. once you've done this you can annotate the prooperty with #DynamoDBTypeConverted(converter = xxx):
In your example:
#DynamoDBTable(tableName = "FulfillmentOrders")
public class FulfillmentOrder {
#DynamoDBHashKey
private String orderId;
#DynamoDBAttribute
#DynamoDBTypeConverted(converter = BodyTypeConverter.class)
private Map<String, Object> body;
}
static public class BodyTypeConverter implements DynamoDBTypeConverter<String, Map<String, Object>> {
#Override
public String convert(Map<String, Object> object) {
DimensionType itemDimensions = (Map<String, Object>) object;
// Convert the object to a DynamoDB json string
String json = "wibble";
return json;
}
#Override
public DimensionType unconvert(String s) {
Map<String, Object> item = new Map<String, Object>();
// Convert s to a Map<String, Object> here.
return item;
}
}
More information can be found here

JObject.ToBsonDocument dropping values

I'm inserting raw JSON into a collection and finding that what is stored in the database is missing the values. For example, my collection is a collection of BsonDocuments:
_products = database.GetCollection<BsonDocument>("products");
The code to insert the JSON into the collection:
public int AddProductDetails(JObject json)
{
var doc = json.ToBsonDocument(DictionarySerializationOptions.Document);
_products.Insert(doc);
}
The JSON that is passed in looks like this:
{
"Id": 1,
"Tags": [
"book",
"database"
],
"Name": "Book Name",
"Price": 12.12
}
But, what is persisted in the collection is just the properties with no values.
{
"_id": {
"$oid": "5165c7e10fdb8c09f446d720"
},
"Id": [],
"Tags": [
[],
[]
],
"Name": [],
"Price": []
}
Why are the values being dropped?
This does what I was expecting.
public int AddProductDetails(JObject json)
{
BsonDocument doc = BsonDocument.Parse(json.ToString());
_products.Insert(doc);
}
I ran into this issue when I had a C# class with a property of type JObject.
My Solution was to create JObjectSerializer for MondoDB and add the attribute to the property so Mongo serializer uses it. I assume if I tried hard enough I could register the below serializer in Mongo as the global one for this type as well.
Register serializer for property processing:
[BsonSerializer(typeof(JObjectSerializer))]
public JObject AdditionalData { get; set; }
The serializer itself:
public class JObjectSerializer : SerializerBase<JObject> // IBsonSerializer<JObject>
{
public override JObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
var myBSONDoc = BsonDocumentSerializer.Instance.Deserialize(context);
return JObject.Parse(myBSONDoc.ToString());
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JObject value)
{
var myBSONDoc = MongoDB.Bson.BsonDocument.Parse(value.ToString());
BsonDocumentSerializer.Instance.Serialize(context, myBSONDoc);
}
}
The problem when using JObject.ToString, BsonDocument.Parse, etc. is the performance is not very good because you do the same operations multiple times, you do string allocations, parsing, etc.
So, I have written a function that converts a JObject to an IEnumerable<KeyValuePair<string, object>> (only using enumerations), which is a type usable by one of the BsonDocument constructors. Here is the code:
public static BsonDocument ToBsonDocument(this JObject jo)
{
if (jo == null)
return null;
return new BsonDocument(ToEnumerableWithObjects(jo));
}
public static IEnumerable<KeyValuePair<string, object>> ToEnumerableWithObjects(this JObject jo)
{
if (jo == null)
return Enumerable.Empty<KeyValuePair<string, object>>();
return new JObjectWrapper(jo);
}
private class JObjectWrapper : IEnumerable<KeyValuePair<string, object>>
{
private JObject _jo;
public JObjectWrapper(JObject jo)
{
_jo = jo;
}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator() => new JObjectWrapperEnumerator(_jo);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public static object ToValue(JToken token)
{
object value;
switch (token.Type)
{
case JTokenType.Object:
value = new JObjectWrapper((JObject)token);
break;
case JTokenType.Array:
value = new JArrayWrapper((JArray)token);
break;
default:
if (token is JValue jv)
{
value = ((JValue)token).Value;
}
else
{
value = token.ToString();
}
break;
}
return value;
}
}
private class JArrayWrapper : IEnumerable
{
private JArray _ja;
public JArrayWrapper(JArray ja)
{
_ja = ja;
}
public IEnumerator GetEnumerator() => new JArrayWrapperEnumerator(_ja);
}
private class JArrayWrapperEnumerator : IEnumerator
{
private IEnumerator<JToken> _enum;
public JArrayWrapperEnumerator(JArray ja)
{
_enum = ja.GetEnumerator();
}
public object Current => JObjectWrapper.ToValue(_enum.Current);
public bool MoveNext() => _enum.MoveNext();
public void Reset() => _enum.Reset();
}
private class JObjectWrapperEnumerator : IEnumerator<KeyValuePair<string, object>>
{
private IEnumerator<KeyValuePair<string, JToken>> _enum;
public JObjectWrapperEnumerator(JObject jo)
{
_enum = jo.GetEnumerator();
}
public KeyValuePair<string, object> Current => new KeyValuePair<string, object>(_enum.Current.Key, JObjectWrapper.ToValue(_enum.Current.Value));
public bool MoveNext() => _enum.MoveNext();
public void Dispose() => _enum.Dispose();
public void Reset() => _enum.Reset();
object IEnumerator.Current => Current;
}
Have you tried using the BsonSerializer?
using MongoDB.Bson.Serialization;
[...]
var document = BsonSerializer.Deserialize<BsonDocument>(json);
BsonSerializer works with strings, so if the JSON argument is a JObject(or JArray, JRaw etc) you have to serialize it with JsonConvert.SerializeObject()
Here is an updated version of Andrew DeVries's answer that includes handling for serializing/deserializing null values.
public class JObjectSerializer : SerializerBase<JObject>
{
public override JObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
if (context.Reader.CurrentBsonType != BsonType.Null)
{
var myBSONDoc = BsonDocumentSerializer.Instance.Deserialize(context);
return JObject.Parse(myBSONDoc.ToStrictJson());
}
else
{
context.Reader.ReadNull();
return null;
}
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JObject value)
{
if (value != null)
{
var myBSONDoc = BsonDocument.Parse(value.ToString());
BsonDocumentSerializer.Instance.Serialize(context, myBSONDoc);
}
else
{
context.Writer.WriteNull();
}
}
}
The ToStrictJson() call is an extension method that wraps the call to the built-in BSON ToJson() method to include setting the output mode to strict. If this is not done, the parsing will fail because BSON type constructors will remain in the JSON output (ObjectId(), for example).
Here is the implementation of ToStrictJson() as well:
public static class MongoExtensionMethods
{
/// <summary>
/// Create a JsonWriterSettings object to use when serializing BSON docs to JSON.
/// This will force the serializer to create valid ("strict") JSON.
/// Without this, Object IDs and Dates are ouput as {"_id": ObjectId(ds8f7s9d87f89sd9f8d9f7sd9f9s8d)}
/// and {"date": ISODate("2020-04-14 14:30:00:000")} respectively, which is not valid JSON
/// </summary>
private static JsonWriterSettings jsonWriterSettings = new JsonWriterSettings()
{
OutputMode = JsonOutputMode.Strict
};
/// <summary>
/// Custom extension method to convert MongoDB objects to JSON using the OutputMode = Strict setting.
/// This ensure that the resulting string is valid JSON.
/// </summary>
/// <typeparam name="TNominalType">The type of object to convert to JSON</typeparam>
/// <param name="obj">The object to conver to JSON</param>
/// <returns>A strict JSON string representation of obj.</returns>
public static string ToStrictJson<TNominalType>(this TNominalType obj)
{
return BsonExtensionMethods.ToJson<TNominalType>(obj, jsonWriterSettings);
}
}
I use the following. It's based on Simon's answer, thanks for the idea, and works in the same way, avoiding unnecessary serialization / deserialization into string.
It's just a bit more compact, thanks to Linq and C# 10:
public static BsonDocument ToBsonDocument(this JObject o) =>
new(o.Properties().Select(p => new BsonElement(p.Name, p.Value.ToBsonValue())));
public static BsonValue ToBsonValue(this JToken t) =>
t switch
{
JObject o => o.ToBsonDocument(),
JArray a => new BsonArray(a.Select(ToBsonValue)),
JValue v => BsonValue.Create(v.Value),
_ => throw new NotSupportedException($"ToBsonValue: {t}")
};
Most of the answers here involve serializing to and then deserializing from a string. Here is a solution that serializes to/from raw BSON instead. It requires the Newtonsoft.Json.Bson nuget package.
using System.IO;
using MongoDB.Bson.Serialization;
using MongoDB.Bson.Serialization.Serializers;
using Newtonsoft.Json;
using Newtonsoft.Json.Bson;
using Newtonsoft.Json.Linq;
namespace Zonal.EventPublisher.Worker
{
public class JObjectSerializer : SerializerBase<JObject>
{
public override JObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args)
{
using (var stream = new MongoDB.Bson.IO.ByteBufferStream(context.Reader.ReadRawBsonDocument()))
using (JsonReader reader = new BsonDataReader(stream))
{
return JObject.Load(reader);
}
}
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JObject value)
{
using (var stream = new MemoryStream())
using (JsonWriter writer = new BsonDataWriter(stream))
{
value.WriteTo(writer);
var buffer = new MongoDB.Bson.IO.ByteArrayBuffer(stream.ToArray());
context.Writer.WriteRawBsonDocument(buffer);
}
}
}
}
Don't forget to register the serializer with:
BsonSerializer.RegisterSerializer(new JObjectSerializer());
After that you can convert your JObject to a BsonDocument by using the MongoDB.Bson.BsonExtensionMethods.ToBsonDocument extension method:
var myBsonDocument = myJObject.ToBsonDocument()
And convert a BsonDocument back to a JObject by using the MongoDB.Bson.Serialization.BsonSerializer class:
var myJObject = BsonSerializer.Deserialize<JObject>(myBsonDocument);

serializing only parts of an object with json

I have an object called MyObject that has several properties. MyList is a list of MyObject that I populate with a linq query and then I serialize MyList into json. I end up with something like this
List<MyObject> MyList = new List<MyObject>();
MyList = TheLinqQuery(TheParam);
var TheJson = new System.Web.Script.Serialization.JavaScriptSerializer();
string MyJson = TheJson.Serialize(MyList);
What I want to do is serialize only parts of MyObject. For instance, I might have Property1, Property2...Propertyn and I want MyJson to only include Property3, Property5 and Property8.
I thought of a way to do this by creating a new object with only the properties I want and from there create a new list for the serialization. Is this the best way or is there a better/faster way?
Thanks.
// simple dummy object just showing what "MyObject" could potentially be
public class MyObject
{
public String Property1;
public String Property2;
public String Property3;
public String Property4;
public String Property5;
public String Property6;
}
// custom converter that tells the serializer what to do when it sees one of
// the "MyObject" types. Use our custom method instead of reflection and just
// dumping properties.
public class MyObjectConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
throw new ApplicationException("Serializable only");
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// create a variable we can push the serailized results to
Dictionary<string, object> result = new Dictionary<string, object>();
// grab the instance of the object
MyObject myobj = obj as MyObject;
if (myobj != null)
{
// only serailize the properties we want
result.Add("Property1", myobj.Property1);
result.Add("Property3", myobj.Property3);
result.Add("Property5", myobj.Property5);
}
// return those results
return result;
}
public override IEnumerable<Type> SupportedTypes
{
// let the serializer know we can accept your "MyObject" type.
get { return new Type[] { typeof(MyObject) }; }
}
}
And then where ever you're serializing:
// create an instance of the serializer
JavaScriptSerializer serializer = new JavaScriptSerializer();
// register our new converter so the serializer knows how to handle our custom object
serializer.RegisterConverters(new JavaScriptConverter[] { new MyObjectConverter() });
// and get the results
String result = serializer.Serialize(MyObjectInstance);

Does a WebMethod always return XML?

ASP.NET [WebMethod], does it always return XML?
I know it can only return serializable data types however can it for instance return a JSON?
As I'm aware of, you can return XML or JSON.
To return JSON add this annotation or your method:
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
And on your class allow ScriptService
[ScriptService]
An example:
[ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public Dictionary<string, object> Test()
{
var ret = new Dictionary<string, object>();
ret.Add("Test", 1);
return ret;
}
// result:
{d:{Test:1}}

Resources