Say i have a JRaw property where i place a Pascal cased JSON into.
public class C
{
public JRaw Prop {get;set;}
}
var a = new JRaw("{\"A\":42}");
var c = new C { Prop = a };
Now when i serialize this i can force c to be lowercase, like so:
var result = JsonConvert.SerializeObject(c, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
// i have "{\"c\":{\"A\":42}}"
However, is there any way to make JRaw also camelcased?
e.g. i want to get
// "{\"c\":{\"a\":42}}"
Here's a way to do it although it may not be the most efficient as it requires you to serialize your object, deserialize, then serialize it again. I believe the problem is the serializer is not detecting your "A" JSON property properly since it's a raw JSON string as opposed to an object property. This will convert your JRaw JSON string to a dynamic object (ExpandoObject) allowing the "A" JSON property to become an object property temporarily. This'll then be picked up by the serializer once the object is serialized, resulting in all nested property keys being camel case.
using System;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System.Dynamic;
public class Program
{
public class C
{
public JRaw Prop { get; set; }
}
public static void Main()
{
var a = new JRaw("{\"A\":42}");
var c = new C { Prop = a };
var camelCaseSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var result = JsonConvert.SerializeObject(c, camelCaseSettings);
var interimObject = JsonConvert.DeserializeObject<ExpandoObject>(result);
result = JsonConvert.SerializeObject(interimObject, camelCaseSettings);
Console.WriteLine(result);
Console.ReadKey();
}
}
Output: {"prop":{"a":42}}
Related
I am looking for the equivalent of Golang's json: "inline" tag with C#'s System.Text.Json.
For example, I have the following class structure in C#:
class Outer{
public string Hello;
public Inner TheInner;
}
class Inner{
public string Earth;
public string Moon;
}
And I want the serialized and deserialized JSON text to be
{
"Hello" : "example_value_01",
"Earth" : "example_value_02",
"Moon" : "example_value_03"
}
In Golang, I can achieve this with the following structure definition
type Outer struct{
Hello string
TheInner Inner `json: "inline"`
}
type Inner struct{
Earth string
Moon string
}
However, I cannot find a decent way to do this in C#'s System.Text.Json.
To reach it in c# , you don't need any custom classes at all,you can use a dictionary
var dict = new Dictionary<string, string> {
{"Hello","example1"},
{"World","example2"}
};
var json= System.Text.Json.JsonSerializer.Serialize(dict,new JsonSerializerOptions { WriteIndented = true});
result
{
"Hello": "example1",
"World": "example2"
}
but if you want it a hard way it is much easier to make it using Newtonsoft.Json since Text.Json needs a custom serializer for almost everything
using Newtonsoft.Json;
var json = SerializeOuterObj(obj, "TheInner");
obj = DeserializeOuterObj(json);
public string SerializeOuterObj(object outerObj, string innerObjectPropertyName)
{
var jsonParsed = JObject.FromObject(outerObj);
var prop = jsonParsed.Properties().Where(i => i.Name == innerObjectPropertyName).First();
prop.Remove();
foreach (var p in ((JObject)prop.Value).Properties())
jsonParsed.Add(p.Name, p.Value);
return jsonParsed.ToString();
}
public Outer DeserializeOuterObj(string json)
{
var jsonParsed = JObject.Parse(json);
var outer = jsonParsed.ToObject<Outer>();
outer.TheInner = jsonParsed.ToObject<Inner>();
return outer;
}
This solution uses reflexion, so is independant of number of string items in the class Inner and independant too of name of Inner Class, just you have to respect if its property or field:
public class Outer
{
public string? Hello;
public Inner? TheInner { get; set; }
}
public class Inner
{
public string? World1;
public string? World2;
public string? World3;
}
the program using the nested class:
var json= "{\"Hello\": \"hello\",\"World1\":\"world1\", \"World2\": \"world2\", \"World3\": \"world3\"}";
var ou = deserialize(json);
json = serialize(ou);
the methods serialize and deserialize:
public Outer deserialize(string json)
{
var fieldhello = typeof(Outer).GetFields().First();
var propinner = typeof(Outer).GetProperties().First();
var subfieldsinner = typeof(Inner).GetFields().ToArray();
var document = JsonDocument.Parse(json);
JsonElement root = document.RootElement;
var outer = new Outer();
var inner = new Inner();
var innerstArr = subfieldsinner.Select(f => (field: f, value: root.TryGetProperty(f.Name, out var item) ? item.GetString() : null));
foreach(var p in innerstArr)
p.field.SetValue(inner, p.value);
string? hellost = root.TryGetProperty(fieldhello.Name, out var item) ? item.GetString() : null;
fieldhello.SetValue(outer, hellost);
propinner.SetValue(outer, inner);
return outer;
}
public string serialize(Outer outer)
{
var fieldhello = outer.GetType().GetFields().First();
var propinner = outer.GetType().GetProperties().First();
var inn = (Inner) propinner.GetValue(outer, null);
var subfieldsinner = inn.GetType().GetFields().ToArray();
using var ms = new MemoryStream();
using var writer = new Utf8JsonWriter(ms);
writer.WriteStartObject();
writer.WriteString(fieldhello.Name, (string?)fieldhello.GetValue(outer));
foreach(var f in subfieldsinner)
writer.WriteString(f.Name, (string?)f.GetValue(inn));
writer.WriteEndObject();
writer.Flush();
string json = Encoding.UTF8.GetString(ms.ToArray());
return json;
}
I have an interface with exposes a property called Pages:
public interface INameSet
{
IQueryable<string> Names { get; }
}
I have this class which implements the interface and must also be parsed from a JSON object:
[DataContract(Name = "surveyPageSet")]
public class SurveyPage : INameSet
{
[DataMember(Name = "names")]
public List<string> SurveyNames { get; set; }
public IQueryable<string> Names
{
get
{
//Returns SurveyNames after some filtration logic
}
}
}
My problem is that when I pass in this object:
{
"names": ["testname"]
}
The JSON interpreter is trying to deserialize it to match the Names property instead of the SurveyNames property. I know this happens because when removing the implementation of the interface and changing SurveyNames to Names it populates the property fine. Is there any way to get it to serialize to the correct property or do I need to create a translator class that will generate the proper concretion of the INameSet interface?
EDIT: This is with the built-in serializer. If there is a solution with Newtonsoft/JSON.NET that would be fine with me.
JavaScriptSerializer doesn't allow for remapping of names out of the box, so don't use it.
Instead, use Json.NET or DataContractJsonSerializer. In fact, both should already work given the data contract attributes you have applied.
For instance, using Json.NET, if I do:
var page1 = JsonConvert.DeserializeObject<SurveyPage>(json);
Debug.Assert(page1.SurveyNames != null && page1.SurveyNames.SequenceEqual(new string [] { "testname" }));
Then there is no assert. Similarly there is no assert if I do:
var page2 = DataContractJsonSerializerHelper.GetObject<SurveyPage>(json);
Debug.Assert(page2.SurveyNames != null && page2.SurveyNames.SequenceEqual(new string[] { "testname" }));
using the helper class:
public static class DataContractJsonSerializerHelper
{
private static MemoryStream GenerateStreamFromString(string value)
{
return new MemoryStream(Encoding.Unicode.GetBytes(value ?? ""));
}
public static string GetJson<T>(T obj, DataContractJsonSerializer serializer)
{
using (var memory = new MemoryStream())
{
serializer.WriteObject(memory, obj);
memory.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memory))
{
return reader.ReadToEnd();
}
}
}
public static string GetJson<T>(T obj) where T : class
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return GetJson(obj, serializer);
}
public static T GetObject<T>(string json) where T : class
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
return GetObject<T>(json, serializer);
}
public static T GetObject<T>(string json, DataContractJsonSerializer serializer)
{
T obj = default(T);
using (var stream = GenerateStreamFromString(json))
{
obj = (T)serializer.ReadObject(stream);
}
return obj;
}
}
Update
If you really want to continue to use JavaScriptConverter, you can write your own JavaScriptConverter and deserialize each field manually. But it's a bother and I wouldn't recommend it.
Using JSON.NET - I'm trying to serialize a mass collection of objects that can contain any other object, that can contain any number of arrays of other objects. Upon doing a serialize and deserialize, the data is not typing correctly/being destroyed. Hours of searches later, can't solve.
public class SubClass
{
public string theString;
}
public class MasterClass
{
public object theObj;
}
Sample code:
SubClass thesubclass = new SubClass(); thesubclass.theString = "TESTSTRING";
MasterClass theMaster = new MasterClass();
theMaster.theObj = thesubclass;
string jsonOut = JsonConvert.SerializeObject(theMaster, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All});
textBox1.Text = jsonOut;
//Out1: {"$type":"WindowsFormsApplication1.MasterClass, WindowsFormsApplication1","theObj":{"$type":"WindowsFormsApplication1.SubClass, WindowsFormsApplication1","theString":"TESTSTRING"}}
MasterClass testMaster = JsonConvert.DeserializeObject<MasterClass>(jsonOut);
string jsonOut2 = JsonConvert.SerializeObject(testMaster, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All});
textBox2.Text = jsonOut2;
//Out2: {"$type":"WindowsFormsApplication1.MasterClass, WindowsFormsApplication1","theObj":{"theString":"TESTSTRING"}}
Basically any object past the main object is losing it's type. //Out2 should match //Out1, but they never do. Help
You need to set the TypeNameHandling setting also when you deserialize:
MasterClass testMaster = JsonConvert.DeserializeObject<MasterClass>(jsonOut, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
I am curious about how the Tuple<T1, T2, T3, ...> serializes and deserializes. I searched using keywords "json" and "tuple" but I could not find what I want.
I test by UnitTest and Json.net, and the test codes is as following. The results shows Tuple<T1,T2,T3,...> is serializable and deserializable. So I can use them in my application.
Test codes
public class Foo {
public List<Tuple<string, string, bool>> Items { get; set; }
public Foo()
{
Items = new List<Tuple<string, string, bool>>();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
foreach (var a in Items)
{
sb.Append(a.Item1 + ", " + a.Item2 + ", " + a.Item3.ToString() + "\r\n");
}
return sb.ToString();
}
}
[TestClass]
public class NormalTests
{
[TestMethod]
public void TupleSerialization()
{
Foo tests = new Foo();
tests.Items.Add(Tuple.Create("one", "hehe", true));
tests.Items.Add(Tuple.Create("two", "hoho", false));
tests.Items.Add(Tuple.Create("three", "ohoh", true));
string json = JsonConvert.SerializeObject(tests);
Console.WriteLine(json);
var obj = JsonConvert.DeserializeObject<Foo>(json);
string objStr = obj.ToString();
Console.WriteLine(objStr);
}
}
Summary
Tuple.Create("own","hehe",true) serializes to {"Item1":"one","Item2":"hehe","Item3":true}
{"Item1":"one","Item2":"hehe","Item3":true} can be deserialized back to Tuple<string,string, bool>
Class Foo with Tuple data, can be serialized to json string, and the string can be deserialized back to Class Foo.
If you are looking for a short answer. I am using JsonConvert.
var testTuple = Tuple.Create(1234, "foo", true);
var serialized = JsonConvert.SerializeObject(testTuple);
Console.WriteLine(serialized);
// prints: {"Item1":1234,"Item2":"foo","Item3":true}
I made a minimal fiddle.
With .NET5 and soon .NET6 it's now recommended to use System.Text.Json over NewtonSoft. The important thing for this serializer with regard to tuples is to set the JsonSerializerOptions option IncludeFields, as otherwise tuple values are excluded by default.
Further, named tuples are just syntactic sugar which are replaced by standard Item1, Item2 notation by the compiler. To include names the simplest way is to use an anonymous object.
Below is a minimal example. (can paste into .NET fiddle with the .NET5 compiler)
using System;
using System.Collections.Generic;
using System.Text.Json;
public class Program
{
public static void Main()
{
JsonSerializerOptions options = new() { IncludeFields = true };
var testTuple = ("test" , "test1", 1324, false);
var serializedTuple = JsonSerializer.Serialize(testTuple, options);
Console.WriteLine(serializedTuple);
var testTuple2 = (NamedItem1: "test" , NamedItemTwo: "test1", TheIntegersName: 1324, ThisBoolHasAFirstNameIts: false);
var serializedTuple2 = JsonSerializer.Serialize(new {testTuple2.NamedItem1, testTuple2.NamedItemTwo, testTuple2.TheIntegersName, testTuple2.ThisBoolHasAFirstNameIts }, options);
Console.WriteLine(serializedTuple2);
}
}
output:
{"Item1":"test","Item2":"test1","Item3":1324,"Item4":false}
{"NamedItem1":"test","NamedItemTwo":"test1","TheIntegersName":1324,"ThisBoolHasAFirstNameIts":false}
Thank you Hinrich to the dotnetfiddle link above.
i used the same link, and got to know how Conversion works between Json objects and Tuples.
Below is the code :
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class Program
{
public static void Main()
{
var testTuple = Tuple.Create<int, string, bool>(1234, "foo", true);
var serialized = JsonConvert.SerializeObject(testTuple);
Console.WriteLine(serialized);
JObject test = ((JObject)JsonConvert.DeserializeObject(serialized));
string strSerialized = JsonConvert.SerializeObject(test);
//Tuple<int, string, bool> testTuple1 = JsonConvert.DeserializeObject<Tuple<int, string, bool>>(serialized); // WORKs
Tuple<int, string, bool> testTuple1 = JsonConvert.DeserializeObject<Tuple<int, string, bool>>(strSerialized); // WORKs
Console.WriteLine(testTuple1.Item1.ToString());
}
}
Hope someone finds this helpful.
I want to be able to convert a List<T> into a specific JSON table-like format. In my case, the T will always be a simple object (no nested properties). Here are two examples to illustrate what I want.
Example #1: List<Person> to JSON
// C# list of Persons
var list = new List<Person>() {
new Person() { First = "Jesse", Last = "Gavin", Twitter = "jessegavin" },
new Person() { First = "John", Last = "Sheehan", Twitter = "johnsheehan" }
};
// I want to transform the list above into a JSON object like so
{
columns : ["First", "Last", "Twitter"],
rows: [
["Jesse", "Gavin", "jessegavin"],
["John", "Sheehan", "johnsheehan"]
]
}
Example #2: List<Address> to JSON
// C# list of Locations
var list = new List<Location>() {
new Location() { City = "Los Angeles", State = "CA", Zip = "90210" },
new Location() { City = "Saint Paul", State = "MN", Zip = "55101" },
};
// I want to transform the list above into a JSON object like so
{
columns : ["City", "State", "Zip"],
rows: [
["Los Angeles", "CA", "90210"],
["Saint Paul", "MN", "55101"]
]
}
Is there a way to tell JSON.net to serialize an object in this manner? If not, how could I accomplish this? Thanks.
UPDATE:
Thanks to #Hightechrider's answer, I was able to write some code that solves the problem.
You can view a working example here https://gist.github.com/1153155
Using reflection you can get a list of properties for the type:
var props = typeof(Person).GetProperties();
Given an instance of a Person p you can get an enumeration of the property values thus:
props.Select(prop => prop.GetValue(p, null))
Wrap those up in a generic method, add your favorite Json serialization and you have the format you want.
Assuming your using .Net 4 this should do everything you want. The class actually lets you convert to either XML or JSON. The Enum for CommunicationType is at the bottom. The serializer works best if the class your passing it has been decorated with DataContract & DataMember attributes. I've included a sample at the bottom. It will also take an anonymous type so long as it's all simple types.
Reflection would work as well but then you have to understand all the JSON nuances to output complex data types, etc. This used the built-in JSON serializer in .Net 4. One more note, because JSON does not define a date type .Net puts dates in a funky ASP.Net custom format. So long as your deserializing using the built-in deserializer it works just fine. I can dig up the documentation on that if you need.
using System;
using System.Xml.Serialization;
using System.Text;
using System.Runtime.Serialization.Json;
using System.IO;
using System.Xml.Linq;
internal class Converter
{
public static string Convert<T>(T obj, CommunicationType format, bool indent = false, bool includetype = false)
{
if (format == CommunicationType.XML)
{
return ToXML<T>(obj, includetype, indent);
}
else if (format == CommunicationType.JSON)
{
return ToJSON<T>(obj);
}
else
{
return string.Empty;
}
}
private static string ToXML<T>(T obj, bool includetype, bool indent = false)
{
if (includetype)
{
XElement xml = XMLConverter.ToXml(obj, null, includetype);
if(indent) {
return xml.ToString();
}
else
{
return xml.ToString(SaveOptions.DisableFormatting);
}
}
else
{
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
XmlSerializer xs = new XmlSerializer(typeof(T));
StringBuilder sbuilder = new StringBuilder();
var xmlws = new System.Xml.XmlWriterSettings() { OmitXmlDeclaration = true, Indent = indent };
ns.Add(string.Empty, string.Empty);
using (var writer = System.Xml.XmlWriter.Create(sbuilder, xmlws))
{
xs.Serialize(writer, obj, ns);
}
string result = sbuilder.ToString();
ns = null;
xs = null;
sbuilder = null;
xmlws = null;
return result;
}
}
private static string ToJSON<T>(T obj)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
using (MemoryStream ms = new MemoryStream())
{
string result = string.Empty;
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
ser.WriteObject(ms, obj);
result = encoding.GetString(ms.ToArray());
ms.Close();
encoding = null;
ser = null;
return result;
}
}
}
[DataContract()]
public enum CommunicationType : int
{
[XmlEnum("0"), EnumMember(Value = "0")]
XML = 0,
[XmlEnum("1"), EnumMember(Value = "1")]
JSON = 1
}
[DataContract(Namespace = "")]
public partial class AppData
{
[DataMember(Name = "ID")]
public string ID { get; set; }
[DataMember(Name = "Key")]
public string Key { get; set; }
[DataMember(Name = "Value")]
public string Value { get; set; }
[DataMember(Name = "ObjectType")]
public string ObjectType { get; set; }
}
Any specific reason why you don't need the standard format?
To actually answer the question:
Since this is something that is outside of JSON syntax I can't think of a way to implement this within the default framework.
One solution would be to leverage attributes decorate the properties you want transported over the wired with a custom attribute and using Reflection cycle through the properties and output their property names as the column headers and then cycle throw the objects and write the values. Generic enough so it could be applied across other objects as well.
public class Location
{
[JsonFooAttribute("City")]
public string city {get;set;}
[JsonFooAttribute("State")]
public string state {get;set;}
}