I am trying to create the following JSON data:
{
'chart.labels': ['Bob','Lucy','Gary','Hoolio'],
'chart.tooltips': ['Bob did well',
'Lucy had her best result',
'Gary - not so good',
'Hoolio had a good start'
]
}
I am using C# and trying to create an object in order to do this.....something like:
public class chart{
public string[] chart.labels {get;set;}
public string[] chart.tooltips {get;set;}
}
but obviously I cannot have properties containing spaces.
How would I go about doing this ?
UPDATE:
Using JamieC's answer the following works perfecly
public virtual ActionResult CompanyStatus()
{
var labelList = new List<string>() { "Bob", "Lucy", "Gary", "Hoolio" };
var tooltipsList = new List<string>() { "Bob did well", "Lucy had her best result", "Gary - not so good", "Hoolio had a good start" };
var cData = new chartData()
{
Labels = labelList.ToArray(),
Tooltips = tooltipsList.ToArray()
};
var serializer = new DataContractJsonSerializer(cData.GetType());
String output;
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, cData);
output = Encoding.Default.GetString(ms.ToArray());
}
return this.Content(output);
}
[DataContract]
public class chartData
{
[DataMember(Name = "chart.labels")]
public string[] Labels { get; set; }
[DataMember(Name = "chart.tooltips")]
public string[] Tooltips { get; set; }
}
}
Which produces:
{"chart.labels":["Bob","Lucy","Gary","Hoolio"],"chart.tooltips":["Bob did well","Lucy had her best result","Gary - not so good","Hoolio had a good start"]}
The usual way to do this is to use a DataContractJsonSerializer to turn your object into Json, and use DataMember attributes to annotate what names to use for properties:
[DataContract]
public class ChartModel{
[DataMember(Name = "chart.labels")]
public string[] Labels {get;set;}
[DataMember(Name = "chart.tooltips")]
public string[] Tooltips {get;set;}
}
I personally use my own ActionResult to wrap up the serialization in MVC:
public class JsonDataContractResult : ActionResult
{
public JsonDataContractResult(Object data)
{
this.Data = data;
}
protected JsonDataContractResult()
{
}
public Object Data { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
Guard.ArgumentNotNull(context, "context");
var serializer = new DataContractJsonSerializer(this.Data.GetType());
String output;
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, this.Data);
output = Encoding.Default.GetString(ms.ToArray());
}
context.HttpContext.Response.ContentType = "application/json";
context.HttpContext.Response.Write(output);
}
}
And return that from a helper method in a base controller:
public abstract class MyBaseController: Controller
{
protected JsonDataContractResult JsonContract(Object data)
{
return new JsonDataContractResult(data);
}
}
Then my controller becomes really simple:
public class SomeController: MyBaseController
{
public ActionResult SomeAction()
{
var model = new ChartModel()
{
Labels = ...,
Tooltips = ...
};
return JsonContract(model);
}
}
You can use JSON.NET library, you can download it from here
It has this feature:
Attribute property name customization
This question will help you:
Json.Net: JsonSerializer-Attribute for custom naming
And you can use DataContractJsonSerializer it provides this feature, but JavaScriptSerializer is not.
For MVC project Newtonsoft.Json library is available.(You have to manually include this for other projects)
So give JsonProperty in the model, like this...
public class ChartModel{
[JsonProperty("chart.labels")]
public string[] Labels {get;set;}
[JsonProperty("chart.tooltips")]
public string[] Tooltips {get;set;}
}
and use Newtonsoft.Json.JsonConvert.SerializeObject(object); or Json.Encode(object) to convert to JSON.
Related
In my code I have to serialize List<IModel> where IModel is the interface for concrete class Model
Here is some pseudo code for them:
public interface IModel
{
string Codice { get; set; }
int Position { get; }
}
[DataContract]
public class Model : IModel
{
public Model(string Codice, int position)
{
this.Codice = Codice;
this.position = position;
}
[DataMember(Name = "codice")]
public string Codice { get; set; }
[DataMember(Name = "position")]
int position;
public int Position { get { return position; } }
}
After reading this post I wrote:
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(List<IModel>), new[] { typeof(Model) });
using (FileStream writer = File.Create(#"c:\temp\modello.json"))
{
jsonSerializer.WriteObject(writer, myList);
}
It works but it is ugly and the output contains a field for the type of the element, "__type":"Model:#SomeProjectName".
Is there another way to easily serialize a list when it is declared as List<InterfaceName> while it contains elements of the only concrete class that implement that interface ? I tried with some casts but I got compilation error or runtime exceptions.
I would like to specify that in my previous implementation I copied all items from List<IModel> into List<Model> which was the type known to DataContractJsonSerializer. In fact I'm sure that any IModel is a Model.
Why do you need an interface for any such implementation?
I would suggest you to generate C# classes for your JSON through http://json2csharp.com/. Once done , paste those classes and find the <RootObject> class and then serialize it using the code you mentioned in your question
Here is a solution with AutoMapper. I've changed your implemented class by adding a private setter to Position. I hope this is OK for your.
List<IModel> sourceList = new List<IModel> {new Model("A", 1), new Model("B", 2)};
AutoMapper.Mapper.Initialize(a => a.CreateMap<IModel, Model>());
List<Model> targetList = AutoMapper.Mapper.Map<List<IModel>, List<Model>>(sourceList);
AutoMapper.Mapper.Initialize(a =>
{
a.CreateMap<Model, Model>();
a.CreateMap<Model, IModel>().ConstructUsing(Mapper.Map<Model, Model>);
});
DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(List<Model>), new[] { typeof(Model) });
using (FileStream writer = File.Create(#"c:\temp\modello.json"))
{
jsonSerializer.WriteObject(writer, targetList);
}
DataContractJsonSerializer js = new DataContractJsonSerializer(typeof(List<Model>));
MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(File.ReadAllText(#"c:\temp\modello.json")));
List<Model> targetListFromFile = (List<Model>)js.ReadObject(ms);
List<IModel> sourceListFromFile = AutoMapper.Mapper.Map<List<Model>, List<IModel>>(targetListFromFile);
Interface:
public interface IModel
{
string Codice { get; set; }
int Position { get; }
}
Class:
[DataContract]
public class Model : IModel
{
public Model(string Codice, int position)
{
this.Codice = Codice;
Position = position;
}
[DataMember(Name = "codice")]
public string Codice { get; set; }
[DataMember(Name = "position")]
public int Position { get; private set; }
}
The file looks like this:
[{
"codice": "A",
"position": 1
},
{
"codice": "B",
"position": 2
}]
I have an Album class which contains title, description and an integer value of the index of a cover image. It also contains an imageList.
I need to serialize this obj and while the imageList is not serializeable but the imageListStream is...
I'm a complete c# noob so would appreciate what ever guidance available.
The desired result would be somethings like:
<Album>
<Title>Album Title</Title>
<Description>Some explanation.</Description>
<CoverImgIndx>2</CoverImgIdx>
<Images>
<Image>
<indx>0</indx>
<filepath>"C:\Images\file1.jpg"</filepath>
</Image>
<Image>
<indx>1</indx>
<filepath>"C:\Images\file2.png"</filepath>
</Image>
<Image>
<indx>2</indx>
<filepath>"C:\Images\file3.jpg"</filepath>
</Image>
<Image>
<indx>3</indx>
<filepath>"C:\Images\file4.bmp"</filepath>
</Image>
</Images>
</Album>
obviously I need to reconstitute the imageList when deserializing...
Sounds like you simply need to use the XmlSerializer class to serialize your Album class into XML.
Something like this should work: https://dotnetfiddle.net/yE8RAl
Generally speaking you can define your Album / Image like this:
[XmlType(TypeName = "Image")]
public class ImageSerializationContainer
{
[XmlElement(ElementName = "indx")]
public int Index { get; set; }
[XmlElement(ElementName = "filepath")]
public string FilePath { get; set; }
}
[XmlType(TypeName = "Album")]
public class AlbumSerializationContainer
{
public string Title { get; set; }
public string Description { get; set; }
public int CoverImgIndx { get; set; }
public List<ImageSerializationContainer> Images { get; set; }
}
And then use XmlSerializer like this:
XmlSerializer ser = new XmlSerializer(typeof(AlbumSerializationContainer));
StringWriter sw = new StringWriter();
ser.Serialize(sw, yourObjectToSerialize);
return sw.ToString();
However I'm guessing that what you really want is to somehow convert your existing class in a form that you can serialize.
Unfortunately you don't give a lot to go on in your question, so I have to make some assumptions. Now, some of this assumptions are bound to be wrong, but that's to be expected with the level of information you have provided.
I'm going to assume that you are using the ImageList class and that the filenames that you have in your example are stored in a string array that is attached to the ImageList Tag property. Feel free to modify the code to take this value from elsewhere. You did not give information about how you structure your index values. I'm assuming these are indexes in the ImageList.Images collection.
There are several ways to approach your problem. I'm going to show you how to use AutoMapper to convert what could be your class to a class that you can serialize and then serialize it.
Below you can find a complete example. Please note that you might need to update the AutoMapper configuration if your classes are different from presented below.
I'm demonstrating both: serializing an class to XML (example 1) and converting another class to serializable class (example 2).
using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Xml.Serialization;
using System.Collections.Generic;
using AutoMapper;
namespace SO24174411
{
class Program
{
static void Main()
{
AlbumSerializationContainer example1 = new AlbumSerializationContainer
{
CoverImgIndx = 2,
Description = "Some explanation.",
Images = new List<ImageSerializationContainer>
{
new ImageSerializationContainer {FilePath = #"C:\Images\file1.jpg", Index = 0},
new ImageSerializationContainer {FilePath = #"C:\Images\file2.png", Index = 1},
new ImageSerializationContainer {FilePath = #"C:\Images\file3.jpg", Index = 2},
new ImageSerializationContainer {FilePath = #"C:\Images\file4.bmp", Index = 3}
},
Title = "Album Title"
};
Console.WriteLine("Example 1");
Console.WriteLine(Serialize(example1));
Album album = new Album
{
CoverImgIndx = 2,
Description = "Some explanation.",
Images = new ImageList(),
Title = "Album Title"
};
SetImages(album.Images, new[]
{
#"C:\Images\file1.jpg",
#"C:\Images\file1.jpg",
#"C:\Images\file2.png",
#"C:\Images\file4.bmp"
});
var example2 = PerformMapping(album);
Console.WriteLine("Example 2");
Console.WriteLine(Serialize(example2));
}
private static AlbumSerializationContainer PerformMapping(Album album)
{
Mapper.CreateMap<Album, AlbumSerializationContainer>();
Mapper.CreateMap<ImageList, List<ImageSerializationContainer>>().ConvertUsing<ImageListconverter>();
AlbumSerializationContainer example2 = Mapper.Map<AlbumSerializationContainer>(album);
return example2;
}
public class ImageListconverter : TypeConverter<ImageList, List<ImageSerializationContainer>>
{
protected override List<ImageSerializationContainer> ConvertCore(ImageList source)
{
if (source == null)
{
return null;
}
List<ImageSerializationContainer> result = new List<ImageSerializationContainer>();
for (int i = 0; i < source.Images.Count; i++)
{
result.Add(new ImageSerializationContainer { FilePath = ((string[])source.Tag)[i], Index = i });
}
return result;
}
}
public static string Serialize(AlbumSerializationContainer a)
{
XmlSerializer ser = new XmlSerializer(typeof(AlbumSerializationContainer));
StringWriter sw = new StringWriter();
ser.Serialize(sw, a);
return sw.ToString();
}
public static void SetImages(ImageList l, string[] names)
{
l.Tag = names;
for(int i=0;i<names.Length;i++)
{
// Aparently you can read names[i] file here if you want
l.Images.Add(new Bitmap(1, 1));
}
}
}
public class Album
{
public string Title { get; set; }
public string Description { get; set; }
public int CoverImgIndx { get; set; }
public ImageList Images { get; set; }
}
[XmlType(TypeName = "Image")]
public class ImageSerializationContainer
{
[XmlElement(ElementName = "indx")]
public int Index { get; set; }
[XmlElement(ElementName = "filepath")]
public string FilePath { get; set; }
}
[XmlType(TypeName = "Album")]
public class AlbumSerializationContainer
{
public string Title { get; set; }
public string Description { get; set; }
public int CoverImgIndx { get; set; }
public List<ImageSerializationContainer> Images { get; set; }
}
}
You will be able to easily de-serialize your xml back to the serialization container class with XmlSerializer.Deserialize. Some more work will be required to map these back to your required class. Since this part depends more than any other on the information you have not provided I leave it as an exercise for the reader.
I'm posting json with variables names with underscores (like_this) and attempting to bind to a model that is camelcased (LikeThis), but the values are unable to be bound.
I know I could write a custom model binder, but since the underscored convention is so common I'd expect that a solution already existed.
The action/model I'm trying to post to is:
/* in controller */
[HttpPost]
public ActionResult UpdateArgLevel(UserArgLevelModel model) {
// do something with the data
}
/* model */
public class UserArgLevelModel {
public int Id { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public int ArgLevelId { get; set; }
}
and the json data is like:
{
id: 420007,
first_name: "Marc",
surname: "Priddes",
arg_level_id: 4
}
(Unfortunately I can't change either the naming of either the json or the model)
You can start writing a custom Json.NET ContractResolver:
public class DeliminatorSeparatedPropertyNamesContractResolver :
DefaultContractResolver
{
private readonly string _separator;
protected DeliminatorSeparatedPropertyNamesContractResolver(char separator)
: base(true)
{
_separator = separator.ToString();
}
protected override string ResolvePropertyName(string propertyName)
{
var parts = new List<string>();
var currentWord = new StringBuilder();
foreach (var c in propertyName)
{
if (char.IsUpper(c) && currentWord.Length > 0)
{
parts.Add(currentWord.ToString());
currentWord.Clear();
}
currentWord.Append(char.ToLower(c));
}
if (currentWord.Length > 0)
{
parts.Add(currentWord.ToString());
}
return string.Join(_separator, parts.ToArray());
}
}
This is for your particular case, becase you need a snake case ContractResolver:
public class SnakeCasePropertyNamesContractResolver :
DeliminatorSeparatedPropertyNamesContractResolver
{
public SnakeCasePropertyNamesContractResolver() : base('_') { }
}
Then you can write a custom attribute to decorate your controller actions:
public class JsonFilterAttribute : ActionFilterAttribute
{
public string Parameter { get; set; }
public Type JsonDataType { get; set; }
public JsonSerializerSettings Settings { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.ContentType.Contains("application/json"))
{
string inputContent;
using (var reader = new StreamReader(filterContext.HttpContext.Request.InputStream))
{
inputContent = reader.ReadToEnd();
}
var result = JsonConvert.DeserializeObject(inputContent, JsonDataType, Settings ?? new JsonSerializerSettings());
filterContext.ActionParameters[Parameter] = result;
}
}
}
And finally:
[JsonFilter(Parameter = "model", JsonDataType = typeof(UserArgLevelModel), Settings = new JsonSerializerSettings { ContractResolver = new SnakeCasePropertyNamesContractResolver() })]
public ActionResult UpdateArgLevel(UserArgLevelModel model) {
{
// model is deserialized correctly!
}
I have an object that contains lists of other objects and that looks like this:
public class MyObject {
public int Prop1 { get; set; }
public List<Object1> TheListOfObject1 { get; set; }
public List<Object2> TheListOfObject2 { get; set; }
public string MyObjectInJson { get; set;}
public void MyObjectToJson()
{
JavascriptSerializer TheSerializer = new JavascriptSerializer();
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object1ToJson() });
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object2ToJson() });
MyObjectInJson = TheSerializer.Serialize(this);
}
Now I have another class that's getting a json string of MyObject and I need to deserializse the string and create a MyObject from the string.
I know there's JSON.net and other library available but I want to use .net's JavascriptSerializer.
Suppose that the converters I have also handle the deserializtion. Do I simply do something like this:
1) add FromJson method to MyObject
public this MyObjectFromJson (string MyObjectInJson)
{
JavascriptSerializer TheSerializer = new JavascriptSerializer();
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object1ToJson() });
TheSerializer.RegisterConverters(new JavaScriptConverter[] { new Object2ToJson() });
this = TheSerializer.DeSerialize(MyObjectInJson);
}
2) and in the calling class write this:
MyObject TheObject = new MyObject();
TheObject = TheObject.MyObjectFromJson(TheIncomingString);
I'm not sure how to proceed. Will this kind of approach work?
Thanks.
Unless your lists of Object1 and Object2 are incredibly complex, you probably don't need the Converters.
Here is a quick sample that I threw together based on your code. Note that the deserializer is static since you will want it to create a new object and you can't set this because it is readonly.
public class MyObject
{
public int Prop1 { get; set; }
public List<Object1> TheListOfObject1 { get; set; }
public List<Object2> TheListOfObject2 { get; set; }
public MyObject()
{
TheListOfObject1 = new List<Object1>();
TheListOfObject2 = new List<Object2>();
}
public string ToJson()
{
JavaScriptSerializer TheSerializer = new JavaScriptSerializer();
return TheSerializer.Serialize(this);
}
public static MyObject FromJson(string sValue)
{
JavaScriptSerializer TheSerializer = new JavaScriptSerializer();
return TheSerializer.Deserialize<MyObject>(sValue);
}
}
This is then called by
MyObject oObject = new MyObject();
oObject.TheListOfObject1.Add(new Object1(1));
oObject.TheListOfObject2.Add(new Object2(2));
oObject.Prop1 = 3;
string sJSON = oObject.ToJson();
System.Diagnostics.Debug.WriteLine(sJSON);
oObject = MyObject.FromJson(sJSON);
System.Diagnostics.Debug.WriteLine(oObject.Prop1);
I defined two classes. First one...
[Serializable]
public class LocalizationEntry
{
public LocalizationEntry()
{
this.CatalogName = string.Empty;
this.Identifier = string.Empty;
this.Translation = new Dictionary<string, string>();
this.TranslationsList = new List<Translation>();
}
public string CatalogName
{
get;
set;
}
public string Identifier
{
get;
set;
}
[XmlIgnore]
public Dictionary<string, string> Translation
{
get;
set;
}
[XmlArray(ElementName = "Translations")]
public List<Translation> TranslationsList
{
get
{
var list = new List<Translation>();
foreach (var item in this.Translation)
{
list.Add(new Translation(item.Key, item.Value));
}
return list;
}
set
{
foreach (var item in value)
{
this.Translation.Add(item.Language, item.Text);
}
}
}
}
...where public List<Translation> TranslationsList is a wrapper for non-serializable public Dictionary<string, string> Translation.
Pair of key and value is defined as follows:
[Serializable]
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language
{
get;
set;
}
[XmlText]
public string Text
{
get;
set;
}
public Translation()
{
}
public Translation(string language, string translation)
{
this.Language = language;
this.Text = translation;
}
}
At last code used to serialize:
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
};
entry.Translation.Add("PL", "jabłko");
entry.Translation.Add("EN", "apple");
entry.Translation.Add("DE", "apfel");
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
}
LocalizationEntry deserializedEntry;
using (FileStream stream = File.Open(#"C:\entry.xml", FileMode.Open))
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
}
}
The problem is that after deserialization deserializedEntry.TranslationsList is empty. I set a breakpoint at setter of LocalizationEntry.TransalionsList and it comes from deserializer empty as well. Product of serialization is of course valid. Is there any gap in my code?
EDIT:
Here is generated XML:
<?xml version="1.0"?>
<LocalizationEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<CatalogName>Catalog</CatalogName>
<Identifier>Id</Identifier>
<Translations>
<Translation lang="PL">jabłko</Translation>
<Translation lang="EN">apple</Translation>
<Translation lang="DE">apfel</Translation>
</Translations>
</LocalizationEntry>
The problem is that your TranslationList property is not being set by the Xml Deserializer. The set method will be hit but only by the call to this.TranslationsList = new List(); in the LocalisationEntry constructor. I'm not yet sure why but I suspect it's because it doesn't know how to convert an array of Translation objects back into a List.
I added the following code and it worked fine:
[XmlArray(ElementName = "Translations")]
public Translation[] TranslationArray
{
get
{
return TranslationsList.ToArray();
}
set
{
TranslationsList = new List<Translation>(value);
}
}
[XmlIgnore]
public List<Translation> TranslationsList
....
I am guessing the problem has to do with this:
public List<Translation> TranslationsList
The get/set operators are designed only for something to get or assign a fully-formed list. If you tried to use this in your own code, for example, every time you would do something like
TranslationsList.Add(item)
It would just create a new list from the existing dictionary and not actually deal with your item. I bet the deserializer works much the same way: uses set to create the new object once, then uses get as it adds each item from the XML. Since all that happens in get is it copies from the dictionary (which is empty when you begin your deserialization) you end up with nothing.
Try replacing this with just a field:
public List<Translation> TranslationsList;
and then explicitly call the code to copy the dictionary to this list before you serialize, and copy it from this list to the dictionary after you deserialize. Assuming that works, you can probably figure out a more seamless way to implement what you're trying to do.
I've created a sample, which will allow you to avoid the unnecessary hidden property when using the XmlSerializer:
class Program
{
static void Main(string[] args)
{
LocalizationEntry entry = new LocalizationEntry()
{
CatalogName = "Catalog",
Identifier = "Id",
Translations =
{
{ "PL", "jabłko" },
{ "EN", "apple" },
{ "DE", "apfel" }
}
};
using (MemoryStream stream = new MemoryStream())
{
XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(stream, entry);
stream.Seek(0, SeekOrigin.Begin);
LocalizationEntry deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream);
serializer.Serialize(Console.Out, deserializedEntry);
}
}
}
public class LocalizationEntry
{
public LocalizationEntry() { this.Translations = new TranslationCollection(); }
public string CatalogName { get; set; }
public string Identifier { get; set; }
[XmlArrayItem]
public TranslationCollection Translations { get; private set; }
}
public class TranslationCollection
: Collection<Translation>
{
public TranslationCollection(params Translation[] items)
{
if (null != items)
{
foreach (Translation item in items)
{
this.Add(item);
}
}
}
public void Add(string language, string text)
{
this.Add(new Translation
{
Language = language,
Text = text
});
}
}
public class Translation
{
[XmlAttribute(AttributeName = "lang")]
public string Language { get; set; }
[XmlText]
public string Text { get; set; }
}
There are some drawbacks when working with the XmlSerializer class itself. The .NET guidelines encourage you the not provide public-setters for collection-properties (like your translation list). But when you look at the code generated by the XmlSerializer, you'll see that it will use the Setter regardless of it is accessible. This results in a compile-error when the interim class is dynamically loaded by the XmlSerializer. The only way to avoid this, is to make the XmlSerializer think, that it can't actually create an instance of the list and thus won't try to call set for it. If the XmlSerializer detects that it can't create an instance it will throw an exception instead of using the Setter and the interim class is compiled successfully. I've used the param-keyword to trick the serializer into thinking that there is no default-constructor.
The only drawback from this solution is that you have to use a non-generic, non-interface type for the property (TranslationCollection) in my example.