How to deserialize two different xmls files into two different class-objects in C#? - c#-2.0

Suppose I have two different xml files as embedded-resource in a same assembly:
x.xml
<car brand="Hummer">
<type ... />
<chasis ... />
</car>
y.xml
<shark species="HammerHead">
<color ... />
<maxLen .... />
</shark>
And I have two classes Car.cs and Shark.cs to help to deserialize them.
What would be the technique to deserialize them into two different and separate objects?
The following code can handle only one type at a time. Isn't it?
string[] manifestResourceNames = assembly.GetManifestResourceNames();
foreach (string mrn in manifestResourceNames)
{
Stream stream = assembly.GetManifestResourceStream(mrn);
XmlSerializer serializer = new XmlSerializer(typeof(Car));
Car car = (Car)serializer.Deserialize(stream);
.... .... ....
}
And, when this code will encounter a Shark-class, it will generate an exception.

For the system to be reliable in any way you need to namespace your XML (you should always be namespacing XML anyway - but I'll save you the rant). Therefore:
<car xmlns="http://schemas.cars.org/car" brand="Hummer">
<type /> <chassis />
</car>
<shark xmlns="http://schemas.ocenia.org/predator">
<lazer-beams>1</lazer-beams>
<awesome>Hell yeah.</awesome>
</shark>
Your C# XML serialization attributes would then become:
[XmlRoot("Car", Namespace=CarNamespaceUri)]
public class Car
{
public const string CarNamespaceUri = "http://schemas.cars.org/car";
// ...
}
Following this you would write something along the lines of a XmlSerializerManager. This would maintain an internal Dictionary<Tuple<string, string>, XmlSerializer> - which you can populate via reflection (look for all types with a XmlRootAttribute applied and create the tuple according to Namespace, LocalName and instantiate the XmlSerializer for that type). This would probably be a static class.
To deserialize any element you simply need to look up its Name and Namespace in the dictionary to retrieve the XmlSerializer instance. For example:
public static T Deserialize<T>(XElement element)
{
return (T)Deserialize(element);
}
public static object Deserialize(XElement element)
{
// Remember to do more elaborate checks etc.
using(var r = element.CreateReader())
{
return _serializers[Tuple.Create(element.Name.NamespaceName, element.Name.LocalName)].Deserialize(r);
}
}

Have a look at the XmlSerializer class. From MSDN:
Serializes and deserializes objects into and from XML documents. The
XmlSerializer enables you to control how objects are encoded into XML.
If you class structure doesn't not exactly match the ML given, i.e. there isn't a correlation between property names and XML elements, you will have to use attributes to provide a suitable mapping.
Regarding your code above, an XmlSerializer instance is constructed such that it serializes / de-serializes a single type. You need to create separate instances of this class, one for car one for shark.

your code can handle one class only.
i used this link to learn about XMLserializer. here you can find some nice examples. in the last example this code appears:
static List<Movie> DeserializeFromXML()
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Movie>));
TextReader textReader = new StreamReader(#"C:\movie.xml");
List<Movie> movies;
movies = (List<Movie>)deserializer.Deserialize(textReader);
textReader.Close();
return movies;
}
you can rebuild it to use your .xml files in this matter:
static List<Car> DeserializeCar()
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Car>));
TextReader textReader = new StreamReader("./car.xml");
List<Car> cars;
cars= (List<Car>)deserializer.Deserialize(textReader);
textReader.Close();
return cars;
}
static List<Car> DeserializeShark()
{
XmlSerializer deserializer = new XmlSerializer(typeof(List<Shark>));
TextReader textReader = new StreamReader("./shark.xml");
List<Shark> shark;
shark= (List<Shark>)deserializer.Deserialize(textReader);
textReader.Close();
return shark;
}
by calling these 2 function you can get both classes

I would create an abstract base class like the following
public abstract class Model<T>
{
public static T GetFromXml(String path)
{
if (!File.Exists(path))
return null;
var serializer = new XmlSerializer(typeof(T));
return (T)serializer.Deserialize(File.OpenRead(path));
}
}
public class Car : Model<Car>
{
public String Brand { get; set; }
public String Type { get; set; }
public String Chasis { get; set; }
}
public class Shark : Model<Shark>
{
public String Species { get; set; }
public String Color { get; set; }
public long MaxLength { get; set; }
}
You could easily access the static method from any subclass
Car myCar = Car.GetFromXml("a.xml");
Shark greatWhite = Shark.GetFromXml("b.xml");
Hope that's what you're lookin for.
Thorsten

Related

Getting hold of custom attribute property decorations in a Swashbuckle document filter

I’m using Swashbuckle 6.1.4 in a .net 5.0 project.
I want to customise the ordering of the elements in a schema. The default order, i.e. that in which the properties are declared) isn’t good because when models extend a base model, the properties of the base model are listed at the bottom.
I’ve managed to apply a document filter to sort properties alphabetically:
public class SchemaSortingFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var descs = context.ApiDescriptions.ToList();
// only applying to the SupporterDTO for now...
string model = "SupporterDTO";
if (swaggerDoc.Components.Schemas.ContainsKey(model))
{
var props = swaggerDoc.Components.Schemas[model].Properties.OrderBy(x => x.Key).ToArray();
swaggerDoc.Components.Schemas[model].Properties.Clear();
foreach (var prop in props)
{
swaggerDoc.Components.Schemas[model].Properties.Add(prop.Key, prop.Value);
}
}
}
}
But what I really want is to use a custom attribute to manage the order. Like this:
public class SwaggerOrderAttribute : Attribute
{
public int Order { get; private set; }
public SwaggerOrderAttribute(int order)
{
Order = order;
}
}
Which I’d use to decorate properties thus:
[SwaggerOrder(1)]
String PropertyZ {get; set;}
[SwaggerOrder(3)]
String PropertyX {get; set;}
[SwaggerOrder(2)]
String PropertyY {get; set;}
My problem is that the list of attribute values that is exposed in my filter via swaggerDoc.Components.Schemas[model].Properties does not include my custom attributes.
During my explorations, and thanks to a hint in this question I’ve tried to get hold of them like this but it didn’t see them:
if (apiDesc.TryGetMethodInfo(out MethodInfo mi))
{
var atts = mi.DeclaringType
.GetCustomAttributes(true)
.OfType<SwaggerOrderAttribute>()
.ToList();
}
How do I bring in my custom attributes for inclusion in my sorting linq query?
You need to implement ISchemaFilter. Then you'll be able to get hold of custom attributes from MemberInfo:
context.MemberInfo.CustomAttributes
Here is an implementation of getting an attribute from MemberInfo:
ISchemaFilter:
public class DefaultValuesSwaggerExtensions : Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
var attributes = context?.MemberInfo?.GetCustomAttributes(true).OfType<SwaggerDefaultValueAttribute>();
}
}
Swagger
services.AddSwaggerGen(c =>
{
c.SchemaFilter<DefaultValuesSwaggerExtensions>();
});

ASP.NET Web-API not serializing readonly property

I migrated an API method from a standard MVC action to the new asp.net Web-API beta and suddenly a read only property is no longer serialized (both returning JSON). Is this expected behaviour?
Edit: Added code sample
I have both Newtonsoft.Json 4.0.8 and System.Json 4.0 referenced through nuget packages
public IQueryable<Car> Gets()
{
return _carRepository.GetCars();
}
public class Car
{
public IEnumerable<Photo> Photos
{
get { return _photos; }
}
public string PreviewImageUrl // No longer serialized
{
get
{
var mainImage = Photos.FirstOrDefault(o => o.IsMainPreview) Photos.FirstOrDefault();
return mainImage != null ? mainImage.Url : (string.Empty);
}
}
}
}
The JsonMediaTypeFormatter that ships with the Beta uses a serializer that does not support read-only properties (since they would not round-trip correctly). We are planning on addressing this for the next realese.
In the mean-time you could use a custom JSON MediaTypeFormatter implementation that uses Json.NET (there's one available here) instead of the built-in formatter.
Update: Also check out Henrik's blog about hooking up a JSON.NET formatter: http://blogs.msdn.com/b/henrikn/archive/2012/02/18/using-json-net-with-asp-net-web-api.aspx
I don't know if this is an expected behavior or not. I would say that this is expected for input parameters (because you cannot set their values) but not for output parameters. So I would say this is a bug for an output parameter. And here's an example illustrating the issue:
Model:
public class Product
{
public Product()
{
Prop1 = "prop1 value";
Prop2 = "prop2 value";
Prop3 = "prop3 value";
}
public string Prop1 { get; set; }
[ReadOnly(true)]
public string Prop2 { get; set; }
public string Prop3 { get; protected set; }
}
Controller:
public class ProductsController : ApiController
{
public Product Get(int id)
{
return new Product();
}
}
Request:
api/products/5
Result:
{"Prop1":"prop1 value","Prop2":"prop2 value"}
So if the property doesn't have a public setter it is not serialized which doesn't seem normal as the Product class is used as output in this case.
I would suggest opening a connect ticket so that Microsoft can fix this before the release or at least tell that this is by design.

Create object with list of properties and pass it to Controller

Perhaps there is an easy solution for my problem but I simply cannot seem to find it. I have read lots of tutorials about Knockout so I get the basics but I ask this question because my entity-structure is a bit more complicated than a person with a name and a list of friends which may or may not be on Twitter (Video on Channel9: Helping you build dynamic JavaScript UIs with MVVM and ASP.NET). Here's my situation:
I have a class PersonnelClass with this basic structure:
[Serializable]
//The interface is for the implementation of 'Name' and 'Description'
public class PersonnelClass : IPersonnelClassOrPerson
{
public PersonnelClass() : this(Guid.NewGuid(), "", "") { }
public PersonnelClass(Guid id, String name, String description = null)
{
if (id == Guid.Empty) { throw new ArgumentNullException("id"); }
Id = id;
Name = name;
Description = description;
Properties = new PropertyCollection();
}
public Guid Id { get; private set; }
public String Name { get; set; }
public String Description { get; set; }
public PropertyCollection Properties { get; private set; }
}
The PropertyCollection class and associated AbstractProperty class look like this:
[Serializable]
public class PropertyCollection: List<AbstractProperty> { }
[Serializable]
public abstract class AbstractProperty: IEntity, IProperty
{
public AbstractProperty(String name, String description = null) : this(Guid.NewGuid(), name, description) { }
public AbstractProperty(Guid id, String name, String description = null)
{
if (id == Guid.Empty) { throw new ArgumentNullException("id"); }
if (String.IsNullOrEmpty(name)) { throw new ArgumentNullException("name"); }
Id = id;
Name = name;
Description = description;
}
public Guid Id { get; private set; }
public String Name { get; private set; }
public String Description { get; private set; }
}
In my Controller, I create an instance of a PersonnelClassViewModel that has this structure:
public class PersonnelClassViewModel
{
public PersonnelClass PersonnelClass { get; set; }
public List<AbstractProperty> Properties { get; set; }
}
I fill this viewmodel with a new PersonnelClass and two test-properties to pass to my View like this:
var properties = new List<AbstractProperty>
{
new TextProperty("prop1", "descr1"),
new TextProperty("prop2", "descr2")
//TextProperty is derived from AbstractProperty
};
var vm = new PersonnelClassViewModel { Properties = properties };
return View(vm);
I get everything in my View as I wanted. From the View I want to create a new PersonnelClass with a set of selected properties. I have the fields for Name and Description and to add the properties I have a ListBox with the properties that already exist (for demo-purpose they came from the controller now). Through a bit of Knockout JavaScript code I can select items from this list and populate an HTML select-control () with the selected properties to add to the PersonnelClass. This all works fine, until I want to build up an object to pass back to the Controller and create the PersonnelClass.
My question is: what Knockout JS code is needed to build up this object and pass it to the Controller by submitting the form and in my Controller how should I receive this object, meaning: what type of object should this be (PersonnelClass, PersonnelClassViewModel, ...) ?
If any more info/code is needed, please do ask. Thanks in advance!
Update after answer of 'B Z':
I followed a few more of Steven Sanderson's tutorials about this to be sure I understand this, especially the one you provided in your answer. Now I have following code in my View to start with:
var initialData = #Html.Raw(new JavaScriptSerializer().Serialize(Model));
var viewModel = {
personnelClassViewModel : ko.mapping.fromJS(initialData),
properties : personnelClassViewModel.Properties,
selectedProperties : ko.observableArray([]),
addedProperties : ko.observableArray([])
};
ko.applyBindings(viewModel);
The variable 'initialData' contains the values I expect it to have but then I get the following error:
Microsoft JScript runtime error: 'personnelClassViewModel' is undefined
I have no clue anymore. Can anyone help me fix this?
Steven Sanderson has an example of how to to work with variable length lists and knockoutjs
http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/
Having said that, I think your problem isn't so much on the knockout side and more on the how to databind the data correctly on the server side. In the link above, Steven uses a FromJson attribute to model bind which you may find useful...
HTH

EF 4: Referencing Non-Scalar Variables Not Supported

I'm using code first and trying to do a simple query, on a List property to see if it contains a string in the filtering list. However I am running into problems. For simplicity assume the following.
public class Person
{
public List<string> FavoriteColors { get; set; }
}
//Now some code. Create and add to DbContext
var person = new Person{ FavoriteColors = new List<string>{ "Green", "Blue"} };
dbContext.Persons.Add(person);
myDataBaseContext.SaveChanges();
//Build
var filterBy = new List<string>{ "Purple", "Green" };
var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
The option I am considering is transforming this to a json serialized string since I can perform a Contains call if FavoriteColors is a string. Alternatively, I can go overboard and create a "Color" entity but thats fairly heavy weight. Unfortunately enums are also not supported.
I think the problem is not the collection, but the reference to matches.
var matches = dbContext.Persons.AsQueryable();
matches = from p in matches
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
If you check out the Known Issues and Considerations for EF4 this is more or less exactly the case mentioned.
Referencing a non-scalar variables,
such as an entity, in a query is not
supported. When such a query executes,
a NotSupportedException exception is
thrown with a message that states
"Unable to create a constant value of
type EntityType.
Also note that it specifically says that referencing a collection of scalar variables is supported (that's new in EF 4 imo).
Having said that the following should work (can't try it out right now):
matches = from p in dbContext.Persons
from color in p.FavoriteColors
where filterBy.Contains(color)
select p;
I decided to experiment by creating a "StringEntity" class to overcome this limitation, and used implicit operators to make nice easy transformations to and from strings. See below for solution:
public class MyClass
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid Id { get; set; }
public List<StringEntity> Animals { get; set; }
public MyClass()
{
List<StringEntity> Animals = List<StringEntity>();
}
}
public class StringEntity
{
[Key, DatabaseGenerated(DatabaseGenerationOption.Identity)]
public Guid Id { get; set; }
public string Value { get; set; }
public StringEntity(string value) { Value = value; }
public static implicit operator string(StringEntity se) { return se.Value; }
public static implicit operator StringEntity(string value) { return new StringEntity(value); }
}
public class MyDbContext : DbContext
{
public DbSet<MyClass> MyClasses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<MyClass>()
.HasMany(x => x.Animals)
.WithMany()
.Map(x =>
{
x.MapLeftKey(l => l.Id, "MyClassId");
x.MapRightKey(r => r.Id, "StringEntityId");
});
}
}
...Everything looked like it was working perfectly with some testing(Albeit heavy), and then I implemented for its original purpose, a Multiselect ListBox in an MVC3 view. For reasons unknown to me, IF the ListBox is assigned the same NAME as an Entity Collection Property, none of your selected items will be loaded.
To demonstrate the following did NOT work:
//Razor View Code
string[] animalOptions = new string[] {"Dog", "Cat", "Goat"};
string[] animalSelections = new string[] {"Dog", "Cat"};
Html.ListBox("Animals", Multiselect(animalOptions, animalSelections));
To get around this limitation, I needed to do four things:
//#1 Unpluralize the ListBox name so that is doesn't match the name Model.Animals
var animalOptions = new string[] {"Dog", "Cat", "Goat"};
#Html.ListBox("Animal", new MultiSelectList(animalOptions, Model.Animals.Select(x => x.Value)))
//#2 Use JQuery to replace the id and name attribute, so that binding can occur on the form post
<script type="text/javascript">
jQuery(function ($) {
$("select#Animal").attr("name", "Animals").attr("id", "Animals");
});
</script>
//#3 Create a model binder class to handle List<StringEntity> objects
public class StringEntityListBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var stringArray = controllerContext.HttpContext.Request.Params.GetValues(bindingContext.ModelName);
return stringArray.Select(x => new StringEntity(x)).ToList();
}
}
//#4 Initialize the binder in your Global.asax setup.
ModelBinders.Binders.Add(typeof(List<StringEntity>), new StringEntityListBinder ());
Note, that the Listbox bug did NOT occur when the property was a List of strings, it just didn't like it when it was a List of entities.

S#arp Architecture Fluent mapping for self referencing entity (Tree structure)

I've come up against a problem in converting my Fluent NH mapping to Sharp Architecture. I like the platform for it's ease, however it seems to handle entity mappings slightly differently to pure Fluent NH.
I have a Entity 'Category' that is a simple tree structure. I have to override the auto-mapping as there is a M:M property that I need to add in (not included in code below).
When I create tests on the repository, the GetAll method returns all Categories as it should, however the Children property just infinitely loops itself. i.e. the list of children for each category only contains itself, in and unending loop.
/// The Entity ///
public class Category : Entity
{
public Category()
{
InitMembers();
}
/// <summary>
/// Creates valid domain object
/// </summary>
public Category(string name)
: this()
{
Name = name;
}
/// <summary>
/// Creates valid domain object
/// </summary>
public Category(string name, int depth)
: this()
{
Name = name;
Depth = depth;
}
private void InitMembers()
{
Children = new List<Category>();
}
[DomainSignature]
[NotNullNotEmpty]
public virtual string Name { get; protected set; }
[DomainSignature]
public virtual int Depth { get; protected set; }
public virtual Category Parent { get; set; }
public virtual IList<Category> Children { get; private set; }
public virtual void AddChild(Category category)
{
category.Parent = this;
Children.Add(category);
}
}
/// The Mapping ///
public class CategoryMap : IAutoMappingOverride<Category>
{
public void Override(AutoMap<Category> mapping)
{
mapping.Id(x => x.Id, "CategoryId")
.WithUnsavedValue(0)
.GeneratedBy.Identity();
mapping.Map(x => x.Name).WithLengthOf(50);
mapping.Map(x => x.Depth);
mapping.HasMany<Category>(x => x.Children)
.Inverse()
.Cascade.All()
.KeyColumnNames.Add("Parent_id")
.AsBag();
}
}
/// The Data Repository Tests ///
[TestFixture]
[Category("DB Tests")]
public class CategoryRepositoryTests : RepositoryTestsBase
{
private readonly IRepository<Category> _repository = new Repository<Category>();
protected override void LoadTestData()
{
CreatePersistedCategory("Root 1");
CreatePersistedCategory("Root 2");
CreatePersistedCategoryWithChildren("Level 1", "Level 2", "Level 3");
}
[Test]
public void CanGetAllCategories()
{
var categories = _repository.GetAll();
categories.ShouldNotBeNull();
categories.Count.ShouldEqual(5);
}
[Test]
public void CanGetCategoryById()
{
var category = _repository.Get(1);
category.Name.ShouldEqual("Root 1");
category.Depth.ShouldEqual(1);
}
[Test]
public void CanGetCategoryChildren()
{
var category = _repository.Get(3);
category.Name.ShouldEqual("Level 1");
category.Depth.ShouldEqual(1);
category.Children.ShouldNotBeNull();
category.Children.Count.ShouldEqual(1);
category.Children[0].Name.ShouldEqual("Level 2");
category.Children[0].Depth.ShouldEqual(2);
category.Children[0].Children.ShouldNotBeNull();
category.Children[0].Children.Count.ShouldEqual(1);
category.Children[0].Children[0].Name.ShouldEqual("Level 3");
category.Children[0].Children[0].Depth.ShouldEqual(3);
}
private void CreatePersistedCategory(string categoryName)
{
var category = new Category(categoryName, 1);
_repository.SaveOrUpdate(category);
FlushSessionAndEvict(category);
}
private void CreatePersistedCategoryWithChildren(string category1, string category2, string category3)
{
var cat1 = new Category(category1, 1);
var cat2 = new Category(category2, 2) { Parent = cat1 };
var cat3 = new Category(category3, 3) { Parent = cat2 };
cat1.AddChild(cat2);
cat2.AddChild(cat3);
_repository.SaveOrUpdate(cat1);
FlushSessionAndEvict(cat1);
}
}
Managed to work it out, after much Mapping tweaking. The Auto-mapping stuff although very cool requires some understanding. RTFM for me...
Right you are, I hadn't yet discovered or understood the Auto-mapping conventions: TableNameConvention, PrimaryKeyConvention, and specifically HasManyConvention. The default S#arp code likes to pluralise its database tables, and have Id columns with the table name prefixed, i.e. CategoryId.
I don't like to pluralise, and I like consistent Id columns, 'Id' suffices. And my foreign key references were different style to, I like Category_id.
public class HasManyConvention : IHasManyConvention
{
public bool Accept(IOneToManyCollectionInstance oneToManyPart)
{
return true;
}
public void Apply(IOneToManyCollectionInstance oneToManyPart)
{
oneToManyPart.KeyColumnNames.Add(oneToManyPart.EntityType.Name + "_id");
}
}
public class PrimaryKeyConvention : IIdConvention
{
public bool Accept(IIdentityInstance id)
{
return true;
}
public void Apply(IIdentityInstance id)
{
id.Column("Id");
}
}
However now this all works a treat but I now have a problem with Many-to-many mappings. It seems S#arp doesn't quite support them yet. My mapping overrides don't seem to work, nothing gets inserted into my mapping table in the database.
See: S#arp Architecture many-to-many mapping overrides not working
I was not able to solve this using fluent conventions and from what I have seen searching around this currently can't be done using conventions. Fluent assumes that a self-referencing tree like this is many-to-many, so in your case I assume you are trying to map a many-to-many relationship and so there should be no problem.
In my case I needed to map it as many-to-one (for a strict hierarchy of nested comments and replies). The only way I could figure out to do this was setting an override for the parent-child class to map that relationship. Fortunately it is very easy.
I would love to know if there is a way to successfully map many-to-one like this with conventions though.

Resources