Error when deserializing JSON list into ICollection in C# - c#

I have three projects :
App.Models: Contains object interfaces.
App.WebAPIService: RESTFul API service using JSON.
App.Client: WPF application that communicates with RESTful API Service.
I use Entity Framework to create interfaces and models.
App.Models is used in the other two projects to create objects.
public interface ISheet
{
int ID { get; set; }
string Name { get; set; }
Nullable<System.DateTime> CreatedDate { get; set; }
string CreatedUser { get; set; }
ICollection<ICategory> Category { get; set; }
}
public interface ICategory
{
int ID { get; set; }
string Name { get; set; }
}
App.WebAPIService:
[DataContract(IsReference = true)]
[KnownType(typeof(Category))]
public partial class Sheet : ISheet
{
#region Primitive Properties
[DataMember]
public int ID
{
get;
set;
}
[DataMember]
public Nullable<System.DateTime> CreatedDate
{
get;
set;
}
[DataMember]
public string CreatedUser
{
get;
set;
}
[DataMember]
public string Name
{
get;
set;
}
#endregion
#region Navigation Properties
[DataMember]
public ICollection<ICategory> Category
{
get
{
if (_category == null)
{
var newCollection = new FixupCollection<ICategory>();
newCollection.CollectionChanged += FixupCategory;
_category = newCollection;
}
return _category;
}
set
{
if (!ReferenceEquals(_category, value))
{
var previousValue = _category as FixupCollection<ICategory>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupCategory;
}
_category = value;
var newValue = value as FixupCollection<ICategory>;
if (newValue != null)
{
newValue.CollectionChanged += FixupCategory;
}
}
}
}
private ICollection<ICategory> _category;
#endregion
#region Association Fixup
private void FixupCategory(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (ICategory item in e.NewItems)
{
if (!item.Sheet.Contains(this))
{
item.Sheet.Add(this);
}
}
}
if (e.OldItems != null)
{
foreach (ICategory item in e.OldItems)
{
if (item.Sheet.Contains(this))
{
item.Sheet.Remove(this);
}
}
}
}
#endregion
}
public class SheetController : ApiController
{
private TORDBEntities db = new TORDBEntities();
/// <summary>
/// GET api/Sheet/5
/// </summary>
/// <param name="id">int</param>
/// <returns>Sheet</returns>
public Sheet GetSheet(int id)
{
Sheet sheet = db.Sheet.Include("Category").Single(s => s.ID == id);
if (sheet == null)
{
throw new HttpResponseException
(Request.CreateResponse(HttpStatusCode.NotFound));
}
return sheet;
}
}
App.Client:
public class Sheet : ISheet
{
public int ID { get; set; }
public DateTime? CreatedDate { get; set; }
public string CreatedUser { get; set; }
public string Name { get; set; }
public ICollection<ICategory> Category { get; set; }
}
class ServiceSheet
{
public Sheet sheet = new Sheet();
public Sheet GetSheet(int id)
{
string url = ConfigurationManager.AppSettings["UrlWebService"];
url += #"api/sheet/" + id;
HttpWebRequest requete = WebRequest.Create(url) as HttpWebRequest;
requete.Method = WebRequestMethods.Http.Get;
requete.BeginGetResponse(new AsyncCallback(this.GetSheetResponseCallback),
requete);
return sheet;
}
private void GetSheetResponseCallback(IAsyncResult ar)
{
//Récupération de l'objet HttpWebRequest
HttpWebRequest requete = (HttpWebRequest)ar.AsyncState;
try
{
using (HttpWebResponse reponse = requete.EndGetResponse(ar)
as HttpWebResponse)
{
using (StreamReader streamReader = new
StreamReader(reponse.GetResponseStream()))
{
string Text = streamReader.ReadToEnd();
sheet = JsonConvert.DeserializeObject<Sheet>(Text);
//Newtonsoft.Json.JsonSerializer serializer = new
Newtonsoft.Json.JsonSerializer();
//serializer.Converters.Add(new DTOJsonConverter());
//Sheet maSheet = serializer.Deserialize(streamReader);
}
}
}
catch (WebException we)
{
if (we.Status == WebExceptionStatus.ProtocolError)
{
HttpWebResponse r = (HttpWebResponse)we.Response;
if (r.StatusCode == HttpStatusCode.NotFound)
MessageBox.Show("Code d'erreur: " + r.StatusCode.ToString());
r.Close();
}
else
MessageBox.Show(we.Message + " " + we.Status.ToString());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Erreur");
}
}
}
I get an error for deserialization category. I have tried the following:
public class Sheet : ISheet
{
public int ID { get; set; }
public DateTime? CreatedDate { get; set; }
public string CreatedUser { get; set; }
public string Name { get; set; }
[JsonConverter(typeof(ConcreteTypeConverter<Category>))]
public ICollection<ICategory> Category { get; set; }
}
public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
//assume we can convert to anything for now
return true;
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
//explicitly specify the concrete type we want to create
return serializer.Deserialize<TConcrete>(reader);
}
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
//use the default serialization - it works fine
serializer.Serialize(writer, value);
}
}
Error:
Unable to cast object of type 'System.Collections.Generic.List1[App.Client.Models.Category]' to
type 'System.Collections.Generic.ICollection1[App.Models.ICategory]'.
And this:
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
//explicitly specify the concrete type we want to create
List<TConcrete> liste = serializer.Deserialize<List<TConcrete>>(reader);
ICollection<TConcrete> coll = new Collection<TConcrete>();
liste.ForEach(delegate(TConcrete obj)
{
coll.Add(obj);
});
return coll;
}
Error:
Unable to cast object of type
'System.Collections.Generic.Collection1[App.Client.Models.Category]'
to type
'System.Collections.Generic.ICollection1[App.Models.ICategory]'.
I think the problem is just ICollection; have you ever encountered this problem, or do you have a better solution?
Answer :
OK, it's my fault !
I forgotten to add this part of code client side :
public class Sheet : ISheet
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<ICategory> Category
{
get
{
if (_category == null)
{
var newCollection = new FixupCollection<ICategory>();
newCollection.CollectionChanged += FixupCategory;
_category = newCollection;
}
return _category;
}
set
{
if (!ReferenceEquals(_category, value))
{
var previousValue = _category as FixupCollection<ICategory>;
if (previousValue != null)
{
previousValue.CollectionChanged -= FixupCategory;
}
_category = value;
var newValue = value as FixupCollection<ICategory>;
if (newValue != null)
{
newValue.CollectionChanged += FixupCategory;
}
}
}
}
private ICollection<ICategory> _category;
private void FixupCategory(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (ICategory item in e.NewItems)
{
if (!item.Sheet.Contains(this))
{
item.Sheet.Add(this);
}
}
}
if (e.OldItems != null)
{
foreach (ICategory item in e.OldItems)
{
if (item.Sheet.Contains(this))
{
item.Sheet.Remove(this);
}
}
}
}
}
public class FixupCollection<T> : ObservableCollection<T>
{
protected override void ClearItems()
{
new List<T>(this).ForEach(t => Remove(t));
}
protected override void InsertItem(int index, T item)
{
if (!this.Contains(item))
{
base.InsertItem(index, item);
}
}
}
Without FixupCategory, it couldn't work.
Hope, this will help some people.
Thanks to those who helped me.

Related

ASP.NET Core Web API : abstract class param binding

I have a DTO which is an abstract class. When I am calling the Web API, I need to pass the child class. I followed this link Binding abstract action parameters in WebAPI but what I am not getting is where to put
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(
new PolymorphicProductConverter()
);
This code in my ASP.NET Core application.
Can anyone please help?
My models are like this
public abstract class HrBuildingComponentV1DTO
{
public HrBuildingComponentV1DTO() { }
public string Id { get; set; }
public string Label { get; set; }
public BuildingComponentType Type { get; set; }
public int? Index { get; set; }
public bool IsFilter { get; set; }
public string SectionId { get; set; }
public HrBuildingComponentV1DTO(HrBuildingComponentsV1 component)
{
Id = component.Id;
Label = component.Label;
Type = component.Type;
Index = component.Index;
IsFilter = component.IsFilter;
SectionId = component.SectionId;
}
public abstract HrBuildingComponentsV1 ToModel();
}
public class HrTextBuildingComponentV1DTO : HrBuildingComponentV1DTO
{
public string Value { get; set; }
public HrTextBuildingComponentV1DTO() : base() { }
public HrTextBuildingComponentV1DTO(HrTextBuildingComponentV1 model) : base(model)
{
Value = model.Value;
}
public override HrBuildingComponentsV1 ToModel()
{
return new HrTextBuildingComponentV1(this);
}
}
Here is my custom converter
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace HumanRisks.API.Helpers
{
public class PolymorphicHrBuildingComponentConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(HrBuildingComponentV1DTO);
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
var obj = JObject.Load(reader);
HrBuildingComponentV1DTO product;
var pt = obj["Type"];
if (pt == null)
{
throw new ArgumentException("Missing productType", "productType");
}
BuildingComponentType productType = pt.Value<BuildingComponentType>();
if (productType == BuildingComponentType.Text)
{
product = new HrTextBuildingComponentV1DTO();
}
else if (productType == BuildingComponentType.TextArea)
{
product = new HrTextAreaBuildingComponentV1DTO();
}
else
{
throw new NotSupportedException("Unknown product type: " + productType);
}
serializer.Populate(obj.CreateReader(), product);
return product;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}

C# Deserializing JSON with Dynamic Keys [duplicate]

how can I deserialize below json structure using newtonsoft json.net in .net.
{
"users" : {
"parentname":"test",
"100034" : {
"name" : "tom",
"state" : "WA",
"id" : "cedf-c56f-18a4-4b1"
},
"10045" : {
"name" : "steve",
"state" : "NY",
"id" : "ebb2-92bf-3062-7774"
},
"12345" : {
"name" : "mike",
"state" : "MA",
"id" : "fb60-b34f-6dc8-aaf7"
}
}
}
I tried below code but its not working. I got error 'Error converting value "test" to type 'ConsoleApplication2.User'. Path 'users.parentname', line 5, position 35.'
class Program
{
static void Main(string[] args)
{
string json = #"
{
""users"": {
""parentname"":""test"",
""10045"": {
""name"": ""steve"",
""state"": ""NY"",
""id"": ""ebb2-92bf-3062-7774""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
}
}
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Please suggest.
You have a couple problems:
Your JSON has an extra level of nesting, with the root object containing a single property "users":
{
"users" : { ... }
}
Your data model needs to reflect this.
Your "users" object has a mixture of known and unknown property names. The question Deserialize json with known and unknown fields addresses a similar situation, however in your case your unknown properties always have a fixed schema and their values should be deserialized into a dictionary of POCOs -- specifically the User class. Therefore the answers there don't quite meet your needs, nor does the build-in functionality [JsonExtensionData].
The following converter allows for unknown properties to be deserialized into a typed container, rather than into an dictionary of arbitrary types:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}
public class TypedExtensionDataConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
JsonProperty GetExtensionJsonProperty(JsonObjectContract contract)
{
try
{
return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single();
}
catch (InvalidOperationException ex)
{
throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var extensionJsonProperty = GetExtensionJsonProperty(contract);
var extensionJProperty = (JProperty)null;
for (int i = jObj.Count - 1; i >= 0; i--)
{
var property = (JProperty)jObj.AsList()[i];
if (contract.Properties.GetClosestMatchProperty(property.Name) == null)
{
if (extensionJProperty == null)
{
extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject());
jObj.Add(extensionJProperty);
}
((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent());
}
}
var value = existingValue ?? contract.DefaultCreator();
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var extensionJsonProperty = GetExtensionJsonProperty(contract);
JObject jObj;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
jObj = JObject.FromObject(value, serializer);
}
var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent();
if (extensionValue != null)
{
for (int i = extensionValue.Count - 1; i >= 0; i--)
{
var property = (JProperty)extensionValue.AsList()[i];
jObj.Add(property.RemoveFromLowestPossibleParent());
}
}
jObj.WriteTo(writer);
}
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
public static class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
public static IList<JToken> AsList(this IList<JToken> container) { return container; }
}
Then use it in your classes as follows:
class RootObject
{
[JsonProperty("users")]
public Users Users { get; set; }
}
[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
public Users()
{
this.UserTable = new Dictionary<string, User>();
}
[JsonProperty("parentname")]
public string ParentName { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, User> UserTable { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
}
I wrote the converter in a fairly general way so it can be reused. A converter that is hardcoded for the Users type would require less code.
Your Json has to look like this:
{
"ParentName":"test",
"users":{
"10045":{
"name":"steve",
"state":"NY",
"id":"ebb2-92bf-3062-7774",
"ParentName":"someOtherName"
}
}
}
In order to deserialize it with your given class structure:
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Now you can deserialize the Json string with:
var root = JsonConvert.DeserializeObject<RootObject>(json);

C# - Deserialize json to dynamic property name [duplicate]

how can I deserialize below json structure using newtonsoft json.net in .net.
{
"users" : {
"parentname":"test",
"100034" : {
"name" : "tom",
"state" : "WA",
"id" : "cedf-c56f-18a4-4b1"
},
"10045" : {
"name" : "steve",
"state" : "NY",
"id" : "ebb2-92bf-3062-7774"
},
"12345" : {
"name" : "mike",
"state" : "MA",
"id" : "fb60-b34f-6dc8-aaf7"
}
}
}
I tried below code but its not working. I got error 'Error converting value "test" to type 'ConsoleApplication2.User'. Path 'users.parentname', line 5, position 35.'
class Program
{
static void Main(string[] args)
{
string json = #"
{
""users"": {
""parentname"":""test"",
""10045"": {
""name"": ""steve"",
""state"": ""NY"",
""id"": ""ebb2-92bf-3062-7774""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
}
}
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Please suggest.
You have a couple problems:
Your JSON has an extra level of nesting, with the root object containing a single property "users":
{
"users" : { ... }
}
Your data model needs to reflect this.
Your "users" object has a mixture of known and unknown property names. The question Deserialize json with known and unknown fields addresses a similar situation, however in your case your unknown properties always have a fixed schema and their values should be deserialized into a dictionary of POCOs -- specifically the User class. Therefore the answers there don't quite meet your needs, nor does the build-in functionality [JsonExtensionData].
The following converter allows for unknown properties to be deserialized into a typed container, rather than into an dictionary of arbitrary types:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}
public class TypedExtensionDataConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
JsonProperty GetExtensionJsonProperty(JsonObjectContract contract)
{
try
{
return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single();
}
catch (InvalidOperationException ex)
{
throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var extensionJsonProperty = GetExtensionJsonProperty(contract);
var extensionJProperty = (JProperty)null;
for (int i = jObj.Count - 1; i >= 0; i--)
{
var property = (JProperty)jObj.AsList()[i];
if (contract.Properties.GetClosestMatchProperty(property.Name) == null)
{
if (extensionJProperty == null)
{
extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject());
jObj.Add(extensionJProperty);
}
((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent());
}
}
var value = existingValue ?? contract.DefaultCreator();
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var extensionJsonProperty = GetExtensionJsonProperty(contract);
JObject jObj;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
jObj = JObject.FromObject(value, serializer);
}
var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent();
if (extensionValue != null)
{
for (int i = extensionValue.Count - 1; i >= 0; i--)
{
var property = (JProperty)extensionValue.AsList()[i];
jObj.Add(property.RemoveFromLowestPossibleParent());
}
}
jObj.WriteTo(writer);
}
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
public static class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
public static IList<JToken> AsList(this IList<JToken> container) { return container; }
}
Then use it in your classes as follows:
class RootObject
{
[JsonProperty("users")]
public Users Users { get; set; }
}
[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
public Users()
{
this.UserTable = new Dictionary<string, User>();
}
[JsonProperty("parentname")]
public string ParentName { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, User> UserTable { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
}
I wrote the converter in a fairly general way so it can be reused. A converter that is hardcoded for the Users type would require less code.
Your Json has to look like this:
{
"ParentName":"test",
"users":{
"10045":{
"name":"steve",
"state":"NY",
"id":"ebb2-92bf-3062-7774",
"ParentName":"someOtherName"
}
}
}
In order to deserialize it with your given class structure:
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Now you can deserialize the Json string with:
var root = JsonConvert.DeserializeObject<RootObject>(json);

How do I get the type of my ReportItems

This is a continuation of another post. I'm trying to create an interface that will let me walk through a collection of objects, and access the name of the properties of the object.
A Report object will have ReportSections. A ReportSection will have an ICollection of items which will change depending on usage.
Here's how I'm trying to define it now.
public interface IReport
{
string ReportName { get; set; }
ICollection<IReportSection> ReportSections { get; }
}
public interface IReportSection
{
string ReportSectionName { get; set; }
ICollection ReportItems { get; }
}
public abstract class ReportBase : IReport
{
virtual public string ReportType { get; set; }
virtual public string ReportName { get; set; }
virtual public ICollection<IReportSection> ReportSections { get; set; }
}
public abstract class ReportSectionBase : IReportSection
{
public string ReportSectionName { get; set; }
public ICollection ReportItems { get; set; }
}
In my code, I would do this:
public class BookAffiliates : ReportSectionBase
{
public override string ReportSectionName { get { return "Book Affiliates"; } }
public override ICollection ReportItems { get; set; }
}
public class SomeClass
{
public ICollection<AuthorsViewModel> Authors { get; set; }
public ICollection<ProjectSubmissionViewModel> Submissions { get; set; }
public string ProcessAuthorsReport()
{
var report = new ContribAuthorsReport{ ReportType = "CSV" };
var authorAffil = new BookAffiliates {ReportItems = Authors };
report.ReportSections.Add(chapAffil);
var submissionAffil = new BookAffiliates {ReportItems = Submissions};
report.ReportSections.Add(submissionAffil );
return RenderReport(report)
}
}
In RenderReport I would like to walk through the collections and use the PropertyNames:
private string RenderReport(ReportBase currentReport)
{
var reportBody = new StringBuilder();
reportBody.Append(currentReport.ReportName + Environment.NewLine + Environment.NewLine);
foreach (var thisSection in currentReport.ReportSections)
{
reportBody.Append(thisSection.ReportSectionName + Environment.NewLine);
/// ---- Here! Here! I don't know what type, I want the
/// code to get the type based on ReportSectionBase<T>
var firstItem = thisSection.ReportItems.OfType<???Type???>().FirstOrDefault();
// I would actually like to go through each property of
// the ReportItem type and list it here.
foreach(var prop in firstItem.GetType().GetProperties())
{
reportBody.AppendLine(string.Format("{0}:{1}" prop.Name, prop.Value));
}
}
return reportBody.ToString();
}
I'm not sure how to best define this. I'm pretty sure I've done it before, but it's not coming to me.
You would use Reflection to do it.
foreach(var prop in thisItem.GetType().GetProperties())
{
reportBody.AppendLine(string.Format("{0}:{1}" prop.Name, prop.Value));
}
Took a while, a lot of questions, and figuring out what I really wanted to ask. I came up with this.
Here are my interfaces and base classes:
public class ReportBase
{
public ReportBase()
{
ReportSections = new List<IReportSection>();
}
public string ReportType { get; set; }
public string ReportName { get; set; }
public ICollection<IReportSection> ReportSections { get; set; }
}
public interface IReportSection
{
string ReportSectionName { get; }
ICollection ReportItems { get; set; }
}
public class ReportSection<T> : IReportSection
{
public string ReportSectionName { get; set; }
public ICollection<T> ReportItems { get; set; }
ICollection IReportSection.ReportItems
{
get { return ReportItems as ICollection; }
set { ReportItems = value as ICollection<T>; }
}
}
I create my objects like this:
public ReportBase GetContribAuthorsReport
(
ICollection<ProjectAffiliateViewModel> projectAffiliates,
ICollection<SubmissionAffiliateViewModel> submissionAffiliates
)
{
//var caReport = new ContributingAffiliates("CSV", projectAffiliates, submissionAffiliates);
var caReport = new ReportBase { ReportType = "CSV", ReportName = "Reviewers' Contact Information" };
caReport.ReportSections.Add(new ReportSection<ProjectAffiliateViewModel> { ReportItems = projectAffiliates });
caReport.ReportSections.Add(new ReportSection<SubmissionAffiliateViewModel> { ReportItems = submissionAffiliates });
return caReport;//.Report;
}
Then I iterate through the objects like this:
public class DownloadCsvActionResult : ActionResult
{
public ReportBase Report { get; set; }
public string fileName { get; set; }
private string ReportData { get; set; }
public DownloadCsvActionResult(ReportBase report, string pFileName)
{
Report = report;
fileName = pFileName;
ReportData = RenderReport();
}
private string RenderReport()
{
var reportBody = new StringBuilder();
reportBody.AppendLine(Report.ReportName);
reportBody.Append(Environment.NewLine + Environment.NewLine);
foreach (var thisSection in Report.ReportSections)
{
reportBody.Append(thisSection.ReportSectionName + Environment.NewLine);
if (thisSection.ReportItems != null)
{
var itemType = thisSection.ReportItems.GetType().GetGenericArguments().Single();
var first = true;
foreach (var prop in itemType.GetProperties())
{
if (!first) reportBody.Append(",");
DisplayAttribute attribute = prop.GetCustomAttributes(typeof(DisplayAttribute), false)
.Cast<DisplayAttribute>()
.SingleOrDefault();
string displayName = (attribute != null) ? attribute.Name : prop.Name;
reportBody.Append(displayName);
first = false;
}
reportBody.Append(Environment.NewLine);
foreach (var thisItem in thisSection.ReportItems)
{
var firstData = true;
foreach (var prop in itemType.GetProperties())
{
if (!firstData) reportBody.Append(",");
reportBody.Append(prop.GetValue(thisItem,null));
firstData = false;
}
reportBody.Append(Environment.NewLine);
}
}
reportBody.Append(Environment.NewLine);
}
return reportBody.ToString();
}
public override void ExecuteResult(ControllerContext context)
{
//Create a response stream to create and write the Excel file
HttpContext curContext = HttpContext.Current;
curContext.Response.Clear();
curContext.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
curContext.Response.Charset = "";
curContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
curContext.Response.ContentType = "application/vnd.ms-excel";
//Write the stream back to the response
curContext.Response.Write(ReportData);
curContext.Response.End();
}
}
This gives me what I need for now. Sorry I wasn't as clear in the first place, and thank you for all your help.

How to configure a Custom Datacontract Serializer or XMLSerializer

Im haveing some xml that have this structure
<Person Id="*****" Name="*****">
<AccessControlEntries>
<AccessControlEntry Id="*****" Name="****"/>
</AccessControlEntries>
<AccessControls />
<IdentityGroups>
<IdentityGroup Id="****" Name="*****" />
</IdentityGroups></Person>
and i also have this entities
[DataContract(IsReference = true)]
public abstract class EntityBase
{
protected bool serializing;
[DataMember(Order = 1)]
[XmlAttribute()]
public string Id { get; set; }
[DataMember(Order = 2)]
[XmlAttribute()]
public string Name { get; set; }
[OnDeserializing()]
public void OnDeserializing(StreamingContext context)
{
this.Initialize();
}
[OnSerializing()]
public void OnSerializing(StreamingContext context)
{
this.serializing = true;
}
[OnSerialized()]
public void OnSerialized(StreamingContext context)
{
this.serializing = false;
}
public abstract void Initialize();
public string ToXml()
{
var settings = new System.Xml.XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true;
var sb = new System.Text.StringBuilder();
using (var writer = System.Xml.XmlWriter.Create(sb, settings))
{
var serializer = new XmlSerializer(this.GetType());
serializer.Serialize(writer, this);
}
return sb.ToString();
}
}
[DataContract()]
public abstract class Identity : EntityBase
{
private EntitySet<AccessControlEntry> accessControlEntries;
private EntitySet<IdentityGroup> identityGroups;
public Identity()
{
Initialize();
}
[DataMember(Order = 3, EmitDefaultValue = false)]
[Association(Name = "AccessControlEntries")]
public EntitySet<AccessControlEntry> AccessControlEntries
{
get
{
if ((this.serializing && (this.accessControlEntries==null || this.accessControlEntries.HasLoadedOrAssignedValues == false)))
{
return null;
}
return accessControlEntries;
}
set
{
accessControlEntries.Assign(value);
}
}
[DataMember(Order = 4, EmitDefaultValue = false)]
[Association(Name = "IdentityGroups")]
public EntitySet<IdentityGroup> IdentityGroups
{
get
{
if ((this.serializing && (this.identityGroups == null || this.identityGroups.HasLoadedOrAssignedValues == false)))
{
return null;
}
return identityGroups;
}
set
{
identityGroups.Assign(value);
}
}
private void attach_accessControlEntry(AccessControlEntry entity)
{
entity.Identities.Add(this);
}
private void dettach_accessControlEntry(AccessControlEntry entity)
{
entity.Identities.Remove(this);
}
private void attach_IdentityGroup(IdentityGroup entity)
{
entity.MemberIdentites.Add(this);
}
private void dettach_IdentityGroup(IdentityGroup entity)
{
entity.MemberIdentites.Add(this);
}
public override void Initialize()
{
this.accessControlEntries = new EntitySet<AccessControlEntry>(
new Action<AccessControlEntry>(this.attach_accessControlEntry),
new Action<AccessControlEntry>(this.dettach_accessControlEntry));
this.identityGroups = new EntitySet<IdentityGroup>(
new Action<IdentityGroup>(this.attach_IdentityGroup),
new Action<IdentityGroup>(this.dettach_IdentityGroup));
}
}
[XmlType(TypeName = "AccessControlEntry")]
public class AccessControlEntry : EntityBase, INotifyPropertyChanged
{
private EntitySet<Service> services;
private EntitySet<Identity> identities;
private EntitySet<Permission> permissions;
public AccessControlEntry()
{
services = new EntitySet<Service>(new Action<Service>(attach_Service), new Action<Service>(dettach_Service));
identities = new EntitySet<Identity>(new Action<Identity>(attach_Identity), new Action<Identity>(dettach_Identity));
permissions = new EntitySet<Permission>(new Action<Permission>(attach_Permission), new Action<Permission>(dettach_Permission));
}
[DataMember(Order = 3, EmitDefaultValue = false)]
public EntitySet<Permission> Permissions
{
get {
if ((this.serializing && (this.permissions.HasLoadedOrAssignedValues == false)))
{
return null;
}
return permissions;
}
set
{
permissions.Assign(value);
}
}
[DataMember(Order = 4, EmitDefaultValue = false)]
public EntitySet<Identity> Identities
{
get
{
if ((this.serializing && (this.identities.HasLoadedOrAssignedValues == false)))
{
return null;
}
return identities;
}
set
{
identities.Assign(identities);
}
}
[DataMember(Order = 5, EmitDefaultValue = false)]
public EntitySet<Service> Services
{
get
{
if ((this.serializing && (this.services.HasLoadedOrAssignedValues == false)))
{
return null;
}
return services;
}
set
{
services.Assign(value);
}
}
private void attach_Permission(Permission entity)
{
entity.AccessControlEntires.Add(this);
}
private void dettach_Permission(Permission entity)
{
entity.AccessControlEntires.Remove(this);
}
private void attach_Identity(Identity entity)
{
entity.AccessControlEntries.Add(this);
}
private void dettach_Identity(Identity entity)
{
entity.AccessControlEntries.Remove(this);
}
private void attach_Service(Service entity)
{
entity.AccessControlEntries.Add(this);
}
private void dettach_Service(Service entity)
{
entity.AccessControlEntries.Remove(this);
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(name));
}
#endregion
public override void Initialize()
{
throw new NotImplementedException();
}
}
[DataContract()]
[XmlType(TypeName = "Person")]
public class Person : Identity
{
private EntityRef<Login> login;
[DataMember(Order = 3)]
[XmlAttribute()]
public string Nombre { get; set; }
[DataMember(Order = 4)]
[XmlAttribute()]
public string Apellidos { get; set; }
[DataMember(Order = 5)]
public Login Login {
get
{
return login.Entity;
}
set
{
var previousValue = this.login.Entity;
if (((previousValue != value) || (this.login.HasLoadedOrAssignedValue == false)))
{
if ((previousValue != null))
{
this.login.Entity = null;
previousValue.Person = null;
}
this.login.Entity = value;
if ((value != null))
value.Person = this;
}
}
}
public override void Initialize()
{
base.Initialize();
}
}
[DataContract()]
[XmlType(TypeName = "Login")]
public class Login : EntityBase
{
private EntityRef<Person> person;
[DataMember(Order = 3)]
public string UserID { get; set; }
[DataMember(Order = 4)]
public string Contrasena { get; set; }
[DataMember(Order = 5)]
public Domain Dominio
{
get;
set;
}
public Person Person
{
get
{
return person.Entity;
}
set
{
var previousValue = this.person.Entity;
if (((previousValue != value) || (this.person.HasLoadedOrAssignedValue == false)))
{
if ((previousValue != null))
{
this.person.Entity = null;
previousValue.Login = null;
}
this.person.Entity = value;
if ((value != null))
value.Login = this;
}
}
}
public override void Initialize()
{
throw new NotImplementedException();
}
}
[DataContract()]
[XmlType(TypeName = "IdentityGroup")]
public class IdentityGroup : Identity
{
private EntitySet<Identity> memberIdentities;
public IdentityGroup()
{
Initialize();
}
public override void Initialize()
{
this.memberIdentities = new EntitySet<Identity>(new Action<Identity>(this.attach_Identity), new Action<Identity>(this.dettach_Identity));
}
[DataMember(Order = 3, EmitDefaultValue = false)]
[Association(Name = "MemberIdentities")]
public EntitySet<Identity> MemberIdentites
{
get
{
if ((this.serializing && (this.memberIdentities.HasLoadedOrAssignedValues == false)))
{
return null;
}
return memberIdentities;
}
set
{
memberIdentities.Assign(value);
}
}
private void attach_Identity(Identity entity)
{
entity.IdentityGroups.Add(this);
}
private void dettach_Identity(Identity entity)
{
entity.IdentityGroups.Remove(this);
}
}
[DataContract()]
[XmlType(TypeName = "Group")]
public class Group : Identity
{
public override void Initialize()
{
throw new NotImplementedException();
}
}
but the ToXml() response something like this
<Person xmlns:xsi="************" xmlns:xsd="******" ID="******" Name="*****"/><AccessControlEntries/></Person>
but what i want is something like this
<Person Id="****" Name="***" Nombre="****">
<AccessControlEntries/>
<IdentityGroups/>
</Person>
Try using the following Serialize method:
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
serializer.Serialize(writer, this, ns);

Categories