c# serialise model to objectContent - c#

I have the following class I want to serialise:
public class UpdateDoorCommand : IXmlSerializable
{
// string such as D1
public string DoorId { get; }
public string Name { get; }
public string Notes { get; }
public UpdateDoorCommand(string doorId, string name, string notes)
{
DoorId = doorId;
Name = name;
Notes = notes;
}
public UpdateDoorCommand()
{
}
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteStartElement("Door");
writer.WriteAttributeString("Address", "D1");
writer.WriteElementString("Name", Name);
writer.WriteElementString("Notes", Notes);
writer.WriteEndElement();
}
}
I want the output to look like this:
<Door Address="D1">
<Name>Name1</Name>
<Notes>Notes1</Notes>
</Door>
I use the following code to serialise the object:
[TestMethod]
public async Task XmlSerialisationTest()
{
var model = new UpdateDoorCommand("D1", "Name1", "Notes1");
var mediaTypeFormatters = new MediaTypeFormatterCollection();
mediaTypeFormatters.XmlFormatter.UseXmlSerializer = true;
mediaTypeFormatters.XmlFormatter.WriterSettings.OmitXmlDeclaration = true;
var content = new ObjectContent<UpdateDoorCommand>(model, mediaTypeFormatters.XmlFormatter);
// this does not look like the type
var str = await content.ReadAsStringAsync();
}
}
However the the output of the serialisation does not give the desired results.
The xml is wrapped in an element with the classname of the object.
How can I get desired xml output using the ObjectContent class?
Note that the code needs a reference to System.Net.Http.Formatting in order to run.

I'm not sure if the two ways are compatible but try this:
[XmlRoot(ElementName = "Door", DataType = "string")]
public class UpdateDoorCommand : IXmlSerializable
{
// *snip*
public void WriteXml(XmlWriter writer)
{
//writer.WriteStartElement("Door");
writer.WriteAttributeString("Address", "D1");
writer.WriteElementString("Name", Name);
writer.WriteElementString("Notes", Notes);
//writer.WriteEndElement();
}
}

Simple with Xml Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
UpdateDoorCommand updateDoorCommand = new UpdateDoorCommand("D1","Name1","Note1");
updateDoorCommand.WriteXml();
}
}
public class UpdateDoorCommand
{
// string such as D1
public string DoorId { get; set; }
public string Name { get; set; }
public string Notes { get; set; }
public UpdateDoorCommand(string doorId, string name, string notes)
{
DoorId = doorId;
Name = name;
Notes = notes;
}
public UpdateDoorCommand()
{
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml()
{
XElement doorCommand = new XElement("Door", new object[] {
new XAttribute("Address", DoorId),
new XElement("Name", Name),
new XElement("Notes1", Notes)
});
}
}
}

Related

How to serialize multi-types Collection with IXmlSerializable

I'm writing an xml that seem decent to me but i have issue reading into last collection.
Xml
<SWorkspace Title="Default" NosWorkSpaceGuid="69c4d731-a44d-4eaf-b61c-12042bfaf714">
<ChartWindow WindowsGuid="93213993-7215-4006-9e05-e30dff98b038" Top="0" Left="NaN" Height="0" Width="NaN">
<ArrayOfBaseContener xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<BaseContener ContenerGuid="393bc0e5-bce0-4446-becd-a8796f18ccb9">
<ArrayOfBaseIndicator>
<BaseIndicator Name="Ohlc" BarType="unknow" type="NosIndicator.Ohlc.Ohlc, NosIndicators, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<ArrayOfBaseRenderableSeriesViewModel>
<BaseRenderableSeriesViewModel xsi:type="CandlestickRenderableSeriesViewModel">
<IncludeRolloverModifier>true</IncludeRolloverModifier>
<IncludeTooltipModifier>true</IncludeTooltipModifier>
<IncludeSeriesValueModifier>true</IncludeSeriesValueModifier>
<IncludeSeriesSelectionModifier>true</IncludeSeriesSelectionModifier>
<IncludeDataPointSelectionModifier>true</IncludeDataPointSelectionModifier>
<IsDigitalLine>false</IsDigitalLine>
<Tag xmlns:q1="http://microsoft.com/wsdl/types/" xsi:type="q1:guid">0e2411a0-7b8f-4788-b636-122a9499a62b</Tag>
<Opacity>1</Opacity>
<IsVisible>true</IsVisible>
<AntiAliasing>true</AntiAliasing>
<Stroke>
<A>255</A>
<R>0</R>
<G>128</G>
<B>0</B>
<ScA>1</ScA>
<ScR>0</ScR>
<ScG>0.21586053</ScG>
<ScB>0</ScB>
</Stroke>
<IsSelected>false</IsSelected>
<StrokeThickness>1</StrokeThickness>
<ResamplingMode>Auto</ResamplingMode>
<XAxisId>DefaultAxisId</XAxisId>
<YAxisId>DefaultAxisId</YAxisId>
<ZeroLineY>0</ZeroLineY>
<DrawNaNAs>Gaps</DrawNaNAs>
<StrokeUp xsi:nil="true"/>
<StrokeDown xsi:nil="true"/>
<DataPointWidth>0.8</DataPointWidth>
</BaseRenderableSeriesViewModel>
</ArrayOfBaseRenderableSeriesViewModel>
</BaseIndicator>
</ArrayOfBaseIndicator>
</BaseContener>
</ArrayOfBaseContener>
</ChartWindow>
</SWorkspace>
here is my IxmlSerialization for class causing the issue.
BaseContener Serialization:
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString(nameof(ContenerGuid), this.ContenerGuid.ToString());
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<BaseIndicator>));
serializer.Serialize(writer, actifIndicators);
}
BaseContener Deserialization:
public void ReadXml(XmlReader reader)
{
ContenerGuid = Guid.Parse(reader.GetAttribute(nameof(ContenerGuid)));
OnInitialization();
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.Name == "BaseIndicator")
{
if (!reader.HasAttributes)
{
MessageBox.Show("No Attributs");
}
else
{
string type = reader.GetAttribute("type");
reader.Read(); // consume the value
if (type != "null")
{
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
var indicator = serializer.Deserialize(reader);
}
}
}
break;
}
}
}
For the indicator deserialisation:
public void WriteXml(XmlWriter writer)
{
Type[] SuportedTypes = new Type[] { typeof(CandlestickRenderableSeriesViewModel), typeof(LineRenderableSeriesViewModel) };
writer.WriteAttributeString(nameof(Name), this.GetType().Name);
writer.WriteAttributeString(nameof(BarType), BarType.ToString());
writer.WriteAttributeString("type", this.GetType().AssemblyQualifiedName);
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<BaseRenderableSeriesViewModel>) , SuportedTypes) ;
serializer.Serialize(writer, SeriesViewModels);
}
public void ReadXml(XmlReader reader)
{
//Deserialization crash before this point
Name = reader.GetAttribute(nameof(Name));
while (reader.Read())
{
switch (reader.NodeType)
{
}
}
}
the error is :
ArrayOfBaseRenderableSeriesViewModel was not expected
Any idea how to fix this?
as correction i will add the main probleme is my object is unknown in deserialization cuz baserenderableViewModel is from an other project i refenced.So i added reference to the namespace scichart that i don't own.
XmlRootAttribute root = new XmlRootAttribute();
XmlAttributeOverrides Xoveride = new XmlAttributeOverrides();
root.Namespace = "http://schemas.abtsoftware.co.uk/scichart";
XmlSerializer serializer = new XmlSerializer(SeriesViewModels.GetType(),Xoveride, SuportedTypes ,root, "http://schemas.abtsoftware.co.uk/scichart") ;
serializer.Serialize(writer, SeriesViewModels);
Try following code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication23
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(SWorkspace));
SWorkspace sWorkspace = (SWorkspace)serializer.Deserialize(reader);
}
}
public class SWorkspace
{
[XmlAttribute()]
public string Title { get; set; }
[XmlAttribute()]
public string NosWorkSpaceGuid { get; set; }
public ChartWindow ChartWindow { get; set; }
}
public class ChartWindow
{
[XmlAttribute()]
public string WindowsGuid { get; set; }
[XmlAttribute()]
public string Top { get; set; }
[XmlAttribute()]
public string Height { get; set; }
[XmlAttribute()]
public string Left { get; set; }
[XmlAttribute()]
public string Width { get; set; }
[XmlArray("ArrayOfBaseContener")]
[XmlArrayItem("BaseContener")]
public List<BaseContener> BaseContener { get; set; }
}
public class BaseContener
{
[XmlAttribute()]
public string ContenerGuid { get; set; }
[XmlArray("ArrayOfBaseIndicator")]
[XmlArrayItem("BaseIndicator")]
public List<BaseIndicator> BaseIndicator { get; set; }
}
public class BaseIndicator
{
[XmlArray("ArrayOfBaseRenderableSeriesViewModel")]
[XmlArrayItem(ElementName = "BaseRenderableSeriesViewModel", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public List<BaseRenderableSeriesViewModel> BaseRenderableSeriesViewModel { get; set; }
}
[XmlInclude(typeof(CandlestickRenderableSeriesViewModel))]
public class BaseRenderableSeriesViewModel
{
}
public class CandlestickRenderableSeriesViewModel : BaseRenderableSeriesViewModel
{
}
}

Load and save to text from IList<T>

Here is my code -
class Appointments:IAppointments
{
private readonly IList<IAppointment> _list = new List<IAppointment>();
public Appointments()
{
}
public bool Load()
{
throw new NotImplementedException();
}
public bool Save()
{
throw new NotImplementedException();
}
public IEnumerable<IAppointment> GetAppointmentsOnDate(DateTime date)
{
throw new NotImplementedException();
}
public int IndexOf(IAppointment item)
{
return _list.IndexOf(item);
}
public void Insert(int index, IAppointment item)
{
_list.Insert(index, item);
}
public void RemoveAt(int index)
{
_list.RemoveAt(index);
}
public IAppointment this[int index]
{
get
{
return _list[index];
}
set
{
_list[index] = value;
}
}
public void Add(IAppointment item)
{
_list.Add(item);
}
public void Clear()
{
_list.Clear();
}
public bool Contains(IAppointment item)
{
return _list.Contains(item);
}
public void CopyTo(IAppointment[] array, int arrayIndex)
{
_list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _list.Count; }
}
public bool IsReadOnly
{
get { return _list.IsReadOnly; }
}
public bool Remove(IAppointment item)
{
return _list.Remove(item);
}
public IEnumerator<IAppointment> GetEnumerator()
{
return _list.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
foreach (IAppointment item in _list)
{
if (item == null)
{
break;
}
yield return item;
}
I would like help on how to do the load and save methods. The save method needs to save to a text file. The load method needs to load from a txt file.
IAppointment interface -
namespace Calendar
{
public interface IAppointment
{
DateTime Start { get; }
int Length { get; }
string DisplayableDescription { get; }
bool OccursOnDate(DateTime date);
}
}
The website is complaining that this is mostly code so I am going to write this pointless sentence until it hopefully goes away. Thank you for your patience.
This is the serialization code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace Calendar
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
//add to appointments to object below
Appointments appointments = new Appointments()
{
appointments = new List<Appointment>() {
new Appointment(){ start = DateTime.Today, length = 2, displayableDescription = "Meeting wiith Joe", occursOnDate = true},
new Appointment(){ start = DateTime.Today.AddDays(2), length = 3, displayableDescription = "Meeting wiith Jane", occursOnDate = false}
}
};
//use this code for writing
XmlSerializer serializer = new XmlSerializer(typeof(Appointments));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, appointments);
writer.Flush();
writer.Close();
writer.Dispose();
//use this code for reading
XmlSerializer xs = new XmlSerializer(typeof(Appointments));
XmlTextReader reader = new XmlTextReader(FILENAME);
Appointments newAppointments = (Appointments)xs.Deserialize(reader);
}
}
[XmlRoot("appointments")]
public class Appointments
{
[XmlElement("appointment")]
public List<Appointment> appointments { get; set; }
}
[XmlRoot("appointment")]
public class Appointment
{
[XmlElement("start")]
public DateTime start { get; set; }
[XmlElement("length")]
public int length { get; set; }
[XmlElement("displayableDescription")]
public string displayableDescription { get; set; }
[XmlElement("occursOnDate")]
public bool occursOnDate { get; set; }
}
}
I would just use Json as it's much cleaner and smaller than XML. So for example:
private IList<IAppointment> _list {get; set;}
public Appointments()
{
_list = Load() ?? new List<IAppointment>();
}
public List<IAppointment> Load()
{
try
{
var json = File.ReadAllText(".Appointments.json");
var list = JsonConvert.DeserializeObject<List<IAppointment>>(json);
return list;
}
catch
{
return null;
}
}
public bool Save()
{
try
{
var json = JsonConvert.SerializeObject(_list);
File.WriteAllText(".Appointments.json",json);
return true;
}
catch
{
return false;
}
}
Don't forget to add Newtonsoft.Json to your project via Nuget:
To install Json.NET, run the following command in the Package Manager Console
PM> Install-Package Newtonsoft.Json -Version 6.0.8
I combined my code into your classes. Will need to be debugged but should help you understand serialization better.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
namespace Calendar
{
class Program
{
static void Main(string[] args)
{
}
}
public class IAppointments : IAppointment
{
public DateTime Start { get; set; }
public int Length { get; set; }
public string DisplayableDescription { get; set; }
public bool OccursOnDate(DateTime date)
{
return true;
}
}
class Appointments : IAppointments
{
const string FILENAME = #"c:\temp\test.xml";
private readonly IList<IAppointment> _list = new List<IAppointment>();
public void Add(IAppointment item)
{
_list.Add(item);
}
public bool Load()
{
XmlSerializer xs = new XmlSerializer(typeof(c_Appointments));
XmlTextReader reader = new XmlTextReader(FILENAME);
c_Appointments newAppointments = (c_Appointments)xs.Deserialize(reader);
foreach (Appointment appointment in newAppointments.appointments)
{
IAppointments newAppointment = new IAppointments();
newAppointment.Start = appointment.start;
newAppointment.Length = appointment.length;
newAppointment.DisplayableDescription = appointment.displayableDescription;
this.Add(newAppointment);
}
return true;
}
public bool Save()
{
c_Appointments appointments = new c_Appointments();
appointments.appointments = new List<Appointment>();
foreach (IAppointment iAppointment in _list)
{
Appointment newAppoint = new Appointment();
appointments.appointments.Add(newAppoint);
newAppoint = (Appointment)iAppointment;
}
XmlSerializer serializer = new XmlSerializer(typeof(c_Appointments));
StreamWriter writer = new StreamWriter(FILENAME);
serializer.Serialize(writer, appointments);
writer.Flush();
writer.Close();
writer.Dispose();
return true;
}
}
public interface IAppointment
{
DateTime Start { get; }
int Length { get; }
string DisplayableDescription { get; }
bool OccursOnDate(DateTime date);
}
[XmlRoot("appointments")]
public class c_Appointments
{
[XmlElement("appointment")]
public List<Appointment> appointments { get; set; }
}
[XmlRoot("appointment")]
public class Appointment
{
[XmlElement("start")]
public DateTime start { get; set; }
[XmlElement("length")]
public int length { get; set; }
[XmlElement("displayableDescription")]
public string displayableDescription { get; set; }
}
}
​

Reading existing json and add new record

I am trying to build an app that will allow users to add new entries to a local json file. I can easily write the record to a file but I cannot get it to update it. Here is the code I have at this point:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Newtonsoft.Json;
...
public partial class frmGvhs : Form
{
List<FacultyMember> memberList = new List<FacultyMember>();
String filename = #"C:\Users\John\test.json";
public frmGvhs()
{
InitializeComponent();
}
private void btnSave_Click(object sender, EventArgs e)
{
FacultyMember member = new FacultyMember();
member.firstName = txtFirstName.Text;
member.lastName = txtLastName.Text;
member.email = txtEmail.Text;
member.ext = txtExt.Text;
member.department = cmbDepartments.Text;
memberList.Add(member);
String json = JsonConvert.SerializeObject(memberList.ToArray());
System.IO.File.WriteAllText(filename, json);
}
private void frmGvhs_Load(object sender, EventArgs e)
{
if (System.IO.File.Exists(filename))
{
System.IO.StreamReader re = new System.IO.StreamReader(filename);
JsonTextReader reader = new JsonTextReader(re);
JsonSerializer se = new JsonSerializer();
object parsedData = se.Deserialize(reader);
String json = JsonConvert.SerializeObject(parsedData);
Console.Write(json);
}
}
}
public class FacultyMember
{
public String firstName { get; set; }
public String lastName { get; set; }
public String email { get; set; }
public String ext { get; set; }
public String department { get; set; }
public FacultyMember()
{
}
}
Now when the app loads up I see the string of the existing json data. So now since its a string I cannot work with it. Do I need to loop through the object [parsedData]? I would like to basically add the existing data to the List<FacultyMember> memberList variable.
Read file content and deserialize to memberList. Then add them and save again.
memberList = JsonConvert.DeserializeObject<List<FacultyMember>>(System.IO.File.ReadAllText(filename));
Example
class Program
{
static List<FacultyMember> memberList = new List<FacultyMember>();
static String filename = #"C:\test.json";
static void Main(string[] args)
{
Save();
Load();
}
static void AddNew()
{
FacultyMember member = new FacultyMember();
member.firstName = "Test";
member.lastName = "Test";
member.email = "test";
member.ext = "test";
member.department = "Test";
memberList.Add(member);
Save();
}
static void Save()
{
String json = JsonConvert.SerializeObject(memberList);
System.IO.File.WriteAllText(filename, json);
}
static void Load()
{
memberList = JsonConvert.DeserializeObject<List<FacultyMember>>(System.IO.File.ReadAllText(filename));
AddNew();
Save();
}
}
A couple of things:
You need to deserialize as the type of object you want then cast the result (in this case an array of FacultyMembers.
When you read from the file you should surround it with a using statement so it closes.
Here is a sample:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Foo
{
static String filename = #"C:\test.json";
public static void Save()
{
FacultyMember member = new FacultyMember();
member.firstName = Guid.NewGuid().ToString();
member.lastName = "Bar";
member.email = "Email";
member.ext = "Ext";
member.department = "Dept";
List<FacultyMember> existing = new List<FacultyMember>();
existing.AddRange(Load());
existing.Add(member);
String json = JsonConvert.SerializeObject(existing.ToArray());
System.IO.File.WriteAllText(filename, json);
}
public static FacultyMember[] Load()
{
if (System.IO.File.Exists(filename))
{
using (System.IO.StreamReader re = new System.IO.StreamReader(filename))
{
JsonTextReader reader = new JsonTextReader(re);
JsonSerializer se = new JsonSerializer();
object obj = se.Deserialize(reader, typeof (FacultyMember[]));
return (FacultyMember[]) obj;
}
}
return new FacultyMember[0];
}
}
public class FacultyMember
{
public String firstName { get; set; }
public String lastName { get; set; }
public String email { get; set; }
public String ext { get; set; }
public String department { get; set; }
public FacultyMember()
{
}
}

XML serialization in .net

I'm trying to serialize an object to meet another systems requirements.
It need to look like this:
<custom-attribute name="Colour" dt:dt="string">blue</custom-attribute>
but instead is looking like this:
<custom-attribute>blue</custom-attribute>
So far I have this:
[XmlElement("custom-attribute")]
public String Colour{ get; set; }
I'm not really sure what metadata I need to achieve this.
You could implement IXmlSerializable:
public class Root
{
[XmlElement("custom-attribute")]
public Colour Colour { get; set; }
}
public class Colour : IXmlSerializable
{
[XmlText]
public string Value { get; set; }
public XmlSchema GetSchema()
{
return null;
}
public void ReadXml(XmlReader reader)
{
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("dt:dt", "", "string");
writer.WriteAttributeString("name", "Colour");
writer.WriteString(Value);
}
}
class Program
{
static void Main()
{
var serializer = new XmlSerializer(typeof(Root));
var root = new Root
{
Colour = new Colour
{
Value = "blue"
}
};
serializer.Serialize(Console.Out, root);
}
}

Deserialize array values to .NET properties using DataContractJsonSerializer

I'm working with the DataContractJsonSerializer in Silverlight 4 and would like to deserialize the following JSON:
{
"collectionname":"Books",
"collectionitems": [
["12345-67890",201,
"Book One"],
["09876-54321",45,
"Book Two"]
]
}
Into classes like the following:
class BookCollection
{
public string collectionname { get; set; }
public List<Book> collectionitems { get; set; }
}
class Book
{
public string Id { get; set; }
public int NumberOfPages { get; set; }
public string Title { get; set; }
}
What's the proper place to extend DataContractJsonSerializer to map the unnamed first array element in "collectionitems" to the Id property of the Book class, the second element to the NumberOfPages property and the final element to Title? I don't have control over the JSON generation in this instance and would like the solution to work with the Silverlight subset of .NET. It would be great if the solution could perform the reverse for serialization as well.
If this weren't Silverlight, you could use IDataContractSurrogate to use object[] (what's actually present in your JSON) instead of Book when serializing/deserializing. Sadly, IDataContractSurrogate (and the overloads of the DataContractJsonSerializer constructor which use it) aren't available in Silverlight.
On Silverlight, here's a hacky but simple workaround. Derive the Book class from a type which imlpements ICollection<object>. Since the type in your serialized JSON is object[], the framework will dutifully serialize it into your ICollection<object>, which in turn you can wrap with your properties.
The easiest (and hackiest) is just to derive from List<object>. This easy hack has the downside that users can modify the underlying list data and mess up your properties. If you're the only user of this code, that might be OK. With a little more work, you can roll your own implementation of ICollection and permit only enough methods to run for serialization to work, and throwing exceptions for the rest. I included code samples for both approaches below.
If the above hacks are too ugly for you, I'm sure there are more graceful ways to handle this. You'd probably want to focus your attention on creating a custom collection type instead of List<Book> for your collectionitems property. This type could contain a field of type List<object[]> (which is the actual type in your JSON) which you might be able to convince the serializer to populate. Then your IList implementation could mine that data into actual Book instances.
Another line of investigation could try casting.For example could you implement an implicit type conversion between Book and string[] and would serialization be smart enough to use it? I doubt it, but it may be worth a try.
Anyway, here's code samples for the derive-from-ICollection hacks noted above. Caveat: I haven't verified these on Silverlight, but they should be using only Silverlight-accessible types so I think (fingers crossed!) it should work OK.
Easy, Hackier Sample
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.IO;
[DataContract]
class BookCollection
{
[DataMember(Order=1)]
public string collectionname { get; set; }
[DataMember(Order = 2)]
public List<Book> collectionitems { get; set; }
}
[CollectionDataContract]
class Book : List<object>
{
public string Id { get { return (string)this[0]; } set { this[0] = value; } }
public int NumberOfPages { get { return (int)this[1]; } set { this[1] = value; } }
public string Title { get { return (string)this[2]; } set { this[2] = value; } }
}
class Program
{
static void Main(string[] args)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(BookCollection));
string json = "{"
+ "\"collectionname\":\"Books\","
+ "\"collectionitems\": [ "
+ "[\"12345-67890\",201,\"Book One\"],"
+ "[\"09876-54321\",45,\"Book Two\"]"
+ "]"
+ "}";
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
BookCollection obj = ser.ReadObject(ms) as BookCollection;
using (MemoryStream ms2 = new MemoryStream())
{
ser.WriteObject(ms2, obj);
string serializedJson = Encoding.UTF8.GetString(ms2.GetBuffer(), 0, (int)ms2.Length);
}
}
}
}
Harder, slightly-less-hacky sample
Here's the second sample, showing a manual implementation of ICollection, which prevents users from accessing the collection-- it supports calling Add() 3 times (during deserialization) but otherwise doesn't allow modification via ICollection<T>. The ICollection methods are exposed using explicit interface implementation and there are attributes on those methods to hide them from intellisense, which should further reduce the hack factor. But as you can see this is a lot more code.
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Diagnostics;
using System.ComponentModel;
[DataContract]
class BookCollection
{
[DataMember(Order=1)]
public string collectionname { get; set; }
[DataMember(Order = 2)]
public List<Book> collectionitems { get; set; }
}
[CollectionDataContract]
class Book : ICollection<object>
{
public string Id { get; set; }
public int NumberOfPages { get; set; }
public string Title { get; set; }
// code below here is only used for serialization/deserialization
// keeps track of how many properties have been initialized
[EditorBrowsable(EditorBrowsableState.Never)]
private int counter = 0;
[EditorBrowsable(EditorBrowsableState.Never)]
public void Add(object item)
{
switch (++counter)
{
case 1:
Id = (string)item;
break;
case 2:
NumberOfPages = (int)item;
break;
case 3:
Title = (string)item;
break;
default:
throw new NotSupportedException();
}
}
[EditorBrowsable(EditorBrowsableState.Never)]
IEnumerator<object> System.Collections.Generic.IEnumerable<object>.GetEnumerator()
{
return new List<object> { Id, NumberOfPages, Title }.GetEnumerator();
}
[EditorBrowsable(EditorBrowsableState.Never)]
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return new object[] { Id, NumberOfPages, Title }.GetEnumerator();
}
[EditorBrowsable(EditorBrowsableState.Never)]
int System.Collections.Generic.ICollection<object>.Count
{
get { return 3; }
}
[EditorBrowsable(EditorBrowsableState.Never)]
bool System.Collections.Generic.ICollection<object>.IsReadOnly
{ get { throw new NotSupportedException(); } }
[EditorBrowsable(EditorBrowsableState.Never)]
void System.Collections.Generic.ICollection<object>.Clear()
{ throw new NotSupportedException(); }
[EditorBrowsable(EditorBrowsableState.Never)]
bool System.Collections.Generic.ICollection<object>.Contains(object item)
{ throw new NotSupportedException(); }
[EditorBrowsable(EditorBrowsableState.Never)]
void System.Collections.Generic.ICollection<object>.CopyTo(object[] array, int arrayIndex)
{ throw new NotSupportedException(); }
[EditorBrowsable(EditorBrowsableState.Never)]
bool System.Collections.Generic.ICollection<object>.Remove(object item)
{ throw new NotSupportedException(); }
}
class Program
{
static void Main(string[] args)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(BookCollection));
string json = "{"
+ "\"collectionname\":\"Books\","
+ "\"collectionitems\": [ "
+ "[\"12345-67890\",201,\"Book One\"],"
+ "[\"09876-54321\",45,\"Book Two\"]"
+ "]"
+ "}";
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
BookCollection obj = ser.ReadObject(ms) as BookCollection;
using (MemoryStream ms2 = new MemoryStream())
{
ser.WriteObject(ms2, obj);
string serializedJson = Encoding.UTF8.GetString(ms2.GetBuffer(), 0, (int)ms2.Length);
}
}
}
}
BTW, the first time I read your quesiton I skipped over the important Silverlight requirement. Oops! Anyway, if not using Silverlight, here's the solution I coded up for that case-- it's much easier and I might as well save it here for any Googlers coming later.
The (on regular .NET framework, not Silverlight) magic you're looking for is IDataContractSurrogate. Implement this interface when you want to substitute one type for another type when serializing/deserializing. In your case you wnat to substitute object[] for Book.
Here's some code showing how this works:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.IO;
using System.Collections.ObjectModel;
[DataContract]
class BookCollection
{
[DataMember(Order=1)]
public string collectionname { get; set; }
[DataMember(Order = 2)]
public List<Book> collectionitems { get; set; }
}
class Book
{
public string Id { get; set; }
public int NumberOfPages { get; set; }
public string Title { get; set; }
}
// A type surrogate substitutes object[] for Book when serializing/deserializing.
class BookTypeSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
// "Book" will be serialized as an object array
// This method is called during serialization, deserialization, and schema export.
if (typeof(Book).IsAssignableFrom(type))
{
return typeof(object[]);
}
return type;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// This method is called on serialization.
if (obj is Book)
{
Book book = (Book) obj;
return new object[] { book.Id, book.NumberOfPages, book.Title };
}
return obj;
}
public object GetDeserializedObject(object obj, Type targetType)
{
// This method is called on deserialization.
if (obj is object[])
{
object[] arr = (object[])obj;
Book book = new Book { Id = (string)arr[0], NumberOfPages = (int)arr[1], Title = (string)arr[2] };
return book;
}
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null; // not used
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
return typeDeclaration; // Not used
}
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null; // not used
}
public object GetCustomDataToExport(System.Reflection.MemberInfo memberInfo, Type dataContractType)
{
return null; // not used
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
return; // not used
}
}
class Program
{
static void Main(string[] args)
{
DataContractJsonSerializer ser =
new DataContractJsonSerializer(
typeof(BookCollection),
new List<Type>(), /* knownTypes */
int.MaxValue, /* maxItemsInObjectGraph */
false, /* ignoreExtensionDataObject */
new BookTypeSurrogate(), /* dataContractSurrogate */
false /* alwaysEmitTypeInformation */
);
string json = "{"
+ "\"collectionname\":\"Books\","
+ "\"collectionitems\": [ "
+ "[\"12345-67890\",201,\"Book One\"],"
+ "[\"09876-54321\",45,\"Book Two\"]"
+ "]"
+ "}";
using (MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(json)))
{
BookCollection obj = ser.ReadObject(ms) as BookCollection;
using (MemoryStream ms2 = new MemoryStream())
{
ser.WriteObject(ms2, obj);
string serializedJson = Encoding.UTF8.GetString(ms2.GetBuffer(), 0, (int)ms2.Length);
}
}
}
}
I find your question very interesting. So I have to spent my time in trying to solve the problem. Currently I received an example which can serialize and deserealize JSON data like following:
{
"collectionname":"Books",
"collectionitems":[
{"book":["12345-67890",201,"Book One"]},
{"book":["09876-54321",45,"Book Two"]}
]
}
the corresponding code of a small console application:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Security.Permissions;
namespace DataContractJsonSerializer {
[DataContract]
class BookCollection {
[DataMember (Order = 0)]
public string collectionname { get; set; }
[DataMember (Order = 1)]
public List<Book> collectionitems { get; set; }
}
[Serializable]
[KnownType (typeof (object[]))]
class Book: ISerializable {
public string Id { get; set; }
public int NumberOfPages { get; set; }
public string Title { get; set; }
public Book () { }
[SecurityPermissionAttribute (SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter)]
protected Book (SerializationInfo info, StreamingContext context) {
// called by DataContractJsonSerializer.ReadObject
Object[] ar = (Object[]) info.GetValue ("book", typeof (object[]));
this.Id = (string)ar[0];
this.NumberOfPages = (int)ar[1];
this.Title = (string)ar[2];
}
[SecurityPermission (SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData (SerializationInfo info, StreamingContext context) {
// called by DataContractJsonSerializer.WriteObject
object[] ar = new object[] { (object)this.Id, (object)this.NumberOfPages, (object)this.Title };
info.AddValue ("book", ar);
}
}
class Program {
static readonly string testJSONdata = "{\"collectionname\":\"Books\",\"collectionitems\":[{\"book\":[\"12345-67890\",201,\"Book One\"]},{\"book\":[\"09876-54321\",45,\"Book Two\"]}]}";
static void Main (string[] args) {
BookCollection test = new BookCollection () {
collectionname = "Books",
collectionitems = new List<Book> {
new Book() { Id = "12345-67890", NumberOfPages = 201, Title = "Book One"},
new Book() { Id = "09876-54321", NumberOfPages = 45, Title = "Book Two"},
}
};
MemoryStream memoryStream = new MemoryStream ();
System.Runtime.Serialization.Json.DataContractJsonSerializer ser =
new System.Runtime.Serialization.Json.DataContractJsonSerializer (typeof (BookCollection));
memoryStream.Position = 0;
ser.WriteObject (memoryStream, test);
memoryStream.Flush();
memoryStream.Position = 0;
StreamReader sr = new StreamReader(memoryStream);
string str = sr.ReadToEnd ();
Console.WriteLine ("The result of custom serialization:");
Console.WriteLine (str);
if (String.Compare (testJSONdata, str, StringComparison.Ordinal) != 0) {
Console.WriteLine ("Error in serialization: unexpected results.");
return;
}
byte[] jsonDataAsBytes = System.Text.Encoding.GetEncoding ("iso-8859-1").GetBytes (testJSONdata);
MemoryStream stream = new MemoryStream (jsonDataAsBytes);
stream.Position = 0;
BookCollection p2 = (BookCollection)ser.ReadObject (stream);
}
}
}
I don't yet tested this approach under Silverlight 4.

Categories

Resources