If the tags didn't give it away, I'm working with C#'s XmlSerializer class.
Say, for example, I have a Person class with various properties including age (int), name (string), and deceased (bool). Is there a way to specify that I don't want to serialize any objects whose deceased flags are true?
Edit: I should have specified, but unfortunately due to the situation I can't really edit my list of objects because it's a member of another class, which is what I'm actually serializing. Are there any other suggestions?
Assuming that you have following type of Class structure(As you specified in the comment)
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public bool Deceased { get; set; }
}
public class Being
{
public string Data { get; set; }
[XmlElement("Human")]
public Person Human { get; set; }
public bool ShouldSerializeHuman()
{
return !this.Human.Deceased;
}
}
Here I have added a method called ShouldSerialize this is called a pattern for XML serialization. Here you can use XmlArray and XmlArrayItem for lists etc.(With given name) then the ShouldSerialize checks if it can be serialized.
Below is the code I used for testing.
private static void Main(string[] args)
{
var livingHuman = new Person() { Age = 1, Name = "John Doe", Deceased = true };
var deadHuman = new Person() { Age = 1, Name = "John Doe", Deceased = false };
XmlSerializer serializer = new XmlSerializer(typeof(Being));
serializer.Serialize(Console.Out, new Being { Human = livingHuman, Data = "new" });
serializer.Serialize(Console.Out, new Being { Human = deadHuman, Data = "old" });
}
And here's the output:
=============================
Update:
If you have list of Person as Humans:
public class Being
{
// [XmlAttribute]
public string Data { get; set; }
// Here add the following attributes to the property
[XmlArray("Humans")]
[XmlArrayItem("Human")]
public List<Person> Humans { get; set; }
public bool ShouldSerializeHumans()
{
this.Humans = this.Humans.Where(x => !x.Deceased).ToList();
return true;
}
}
Sample Test:
private static void Main(string[] args)
{
var livingHuman = new Person() { Age = 1, Name = "John Doe", Deceased = true };
var deadHuman = new Person() { Age = 1, Name = "John Doe", Deceased = false };
var humans = new List<Person> { livingHuman, deadHuman };
XmlSerializer serializer = new XmlSerializer(typeof(Being));
serializer.Serialize(Console.Out, new Being() { Humans = humans, Data = "some other data" });
}
Output:
If you have a list of Person objects and only want to serialise some of them, then just filter out the ones you don't need. For example:
List<Person> people = GetPeople(); //from somewhere
List<Person> filteredPeople = people.Where(p => !p.Deceased);
Now you only need to serialise filteredPeople.
Related
I am wondering what is the difference between these two serializers. when setting accept header = application/xml. Am using Plain DTOs as return values, Which one is preferred? Also consumer of the api who request xml in response which should be used?
Am working on aspnet core web api 3.1, building restful apis. Any suggestions/redirects on the above query will be helpful.
The XmlSerializerOutputFormatter is an asp.net core outputformatter that uses the XmlSerializer internally, whereas the DataContractSerializerOutputFormatter uses the DataContractSerializer internally.
The DataContractSerializer is more flexible in configuration. For example it supports reference detection to prevent the serializer from recursively serializing items, which would normally cause an endless loop.
In my own projects, I prefer to use the DataContractSerializerOutputFormatter because it's able to cope with properties with private setter
public string Text { get; private set; }
Failing case
Dtos project
namespace DataContractSerializerPOC.Dtos
{
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Fullname can only be set from this project
public string FullName { get; internal set; }
}
public class PersonService
{
public List<Person> GetPeople()
{
// Create a list of people to be serialized
var people = new List<Person>
{
new Person { Id = 1, FirstName = "John", LastName = "Doe" },
new Person { Id = 2, FirstName = "Jim", LastName = "Flix" },
new Person { Id = 3, FirstName = "Jack", LastName = "Splick" },
};
// Set the fullname from this project
// In some cases you may need to do this, instead of implementing a readonly property
foreach (var person in people)
person.FullName = $"{person.FirstName} {person.LastName}";
return people;
}
}
}
Console project
namespace DataContractSerializerPOC
{
class Program
{
static void Main(string[] args)
{
var personService = new PersonService();
var people = personService.GetPeople();
var writer = new StringWriter();
var serializer = new XmlSerializer(typeof(List<Person>));
serializer.Serialize(writer, people);
}
}
}
Result
Working case with DataContractSerializer
Dtos project
namespace DataContractSerializerPOC.Dtos
{
[DataContract]
public class Person
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
// Fullname can only be set from this project
[DataMember]
public string FullName { get; internal set; }
}
public class PersonService
{
...
}
}
Console project
namespace DataContractSerializerPOC
{
class Program
{
static void Main(string[] args)
{
var personService = new PersonService();
var people = personService.GetPeople();
var memoryStream = new MemoryStream();
var serializer = new DataContractSerializer(typeof(List<Person>));
serializer.WriteObject(memoryStream, people);
memoryStream.Seek(0, SeekOrigin.Begin);
var text = new StreamReader(memoryStream).ReadToEnd();
}
}
}
Result
So the DataContractSerializer is able to deal with properties with a private setter, whilst the XmlSerializer isn't.
I have a simple class with two properties and the Equals method overridden:
public class Person : IEquatable<Person>
{
public Guid Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
return other != null &&
this.Id.Equals(other.Id);
}
public override int GetHashCode()
{
return 2108858624 + EqualityComparer<Guid>.Default.GetHashCode(this.Id);
}
}
Now I created a simple test, where the Id values are the same, but the Name values are different.
[Fact]
public void PersonShouldNotBeEqual()
{
var guid = Guid.NewGuid();
var p1 = new Person { Id = guid, Name = "Me" };
var p2 = new Person { Id = guid, Name = "You" };
p1.Should().NotBeEquivalentTo(p2); // Fails
}
I understood from the documentation that BeEquivalentTo() uses the Equals() method by default when it is overridden in the class, but I haven't found a way to overrule that so the instances are compared by their property values.
Is it possible to do this in FluentAssertions other then the way below?
[Fact]
public void PersonShouldBeEqual()
{
var guid = Guid.NewGuid();
var p1 = new Person { Id = guid, Name = "Me" };
var p2 = new Person { Id = guid, Name = "You" };
p1.Id.Should().Be(p2.Id);
p1.Name.Should().Be(p2.Name);
}
You just need to override equality comparer for your type in EquivalencyAssertionOptions like this :
p1.Should().BeEquivalentTo(p2, options => options.ComparingByMembers<Person>())
You can also use dynamic objects.
[Fact]
public void PersonShouldNotBeEqual()
{
var guid = Guid.NewGuid();
var p1 = new Person { Id = guid, Name = "Me" };
dynamic p2 = new { Id = guid, Name = "You" };
p1.Should().NotBeEquivalentTo(p2);
dynamic p3 = new { Id = guid, Name = "Me" };
p1.Should().BeEquivalentTo(p3);
}
If you also need nested object comparison, use dynamic for them too.
dynamic expected = new
{
Id = 123,
Name = "John Doe",
City = new {
Id = 456,
Name = "Paris"
Country = new {
Id = 789,
Name = France
}
}
};
I am using following code for Student and Employee class derived from Person.
[XmlInclude(typeof(Student)), XmlInclude(typeof(Employee))]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
[XmlType("Student")]
public class Student : Person
{
public int StudentId { get; set; }
public List<string> Subjects { get; set; }
}
[XmlType("Employee")]
public class Employee : Person
{
public int EmployeeId { get; set; }
public float Experience { get; set; }
}
I am creating a list of Person classes and initiate with following values and generating XML by serializing the same.
public class TestSerialization
{
List<Person> lstPerson = new List<Person>();
public void XMLGen()
{
Person obj = new Person() { Name = "Person1", Age = 10, Gender = "M" };
Person obj2 = new Person() { Name = "Person2", Age = 10, Gender = "F" };
Student objS = new Student()
{
Name = "Student1",
Age = 20,
Gender = "M",
StudentId = 1,
Subjects = new List<string>() { "Math", "Science" }
};
Student objS2 = new Student()
{
Name = "Student2",
Age = 15,
Gender = "F",
StudentId = 1,
Subjects = new List<string>() { "Physics", "Chemistry" }
};
Employee objE = new Employee()
{
Name = "Employee1",
Age = 15,
Gender = "F",
EmployeeId = 1,
Experience = 5.5f
};
Employee objE2 = new Employee()
{
Name = "Employee2",
Age = 15,
Gender = "M",
EmployeeId = 2,
Experience = 6.5f
};
lstPerson.Add(obj);
lstPerson.Add(obj2);
lstPerson.Add(objS);
lstPerson.Add(objS2);
lstPerson.Add(objE);
lstPerson.Add(objE2);
Type[] types = { typeof(Student), typeof(Employee) };
XmlSerializer objXml = new XmlSerializer(typeof(List<Person>), types);
using (StringWriter textWriter = new StringWriter())
{
objXml.Serialize(textWriter, lstPerson);
string aa = textWriter.ToString();
}
}
}
But XML that generated contains derived class name as xsi:type="Student" and xsi:type="Employee" as shown below.
<Person xsi:type="Student">
<Name>Student1</Name>
<Age>20</Age>
<Gender>M</Gender>
<StudentId>1</StudentId>
<Subjects>
<string>Math</string>
<string>Science</string>
</Subjects>
and for Employee it is
<Person xsi:type="Employee">
<Name>Employee2</Name>
<Age>15</Age>
<Gender>M</Gender>
<EmployeeId>2</EmployeeId>
<Experience>6.5</Experience>
Is it possible the we get XML node name as Student,Employee rather than Person with xsi:type?
I want XML should be like this.
<Employee>
<Name>Employee2</Name>
<Age>15</Age>
<Gender>M</Gender>
<EmployeeId>2</EmployeeId>
<Experience>6.5</Experience>
The xsi:type is needed for deserialization, when you try to read from the XML again. Otherwise the deserializer wouldn't know what class to deserialize to.
If you are not planning to deserialize into a class again, then you could add your own custom serializer.
Adding xmlroot should give the root tag as employee. You cannot eliminate the type attribute because of the inheritance in the class structure.
[XmlInclude(typeof(Student)), XmlInclude(typeof(Employee))]
[XmlRoot("Employee")]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
[XmlType("Student")]
public class Student : Person
{
public int StudentId { get; set; }
public List<string> Subjects { get; set; }
}
[XmlType("Employee")]
public class Employee : Person
{
public int EmployeeId { get; set; }
public float Experience { get; set; }
}
I got the solution from https://msdn.microsoft.com/en-us/library/3z3z5s6h%28v=vs.110%29.aspx link. Use XmlElementAttribute to change element name and add the XmlElementAttribute to a XmlAttributes instance. Then add the XmlAttributes to a XmlAttributeOverrides instance, specifying the type being overridden and the name of the member that accepts the derived class.
public class Orders
{
public List<Person> Persons;
}
public void SerializeObject(string filename)
{
// Each overridden field, property, or type requires
// an XmlAttributes instance.
XmlAttributes attrs = new XmlAttributes();
// Creates an XmlElementAttribute instance to override the
// field that returns Book objects. The overridden field
// returns Expanded objects instead.
XmlElementAttribute attr = new XmlElementAttribute();
attr.ElementName = "Student";
attr.Type = typeof(Student);
XmlElementAttribute attrE = new XmlElementAttribute();
attrE.ElementName = "Employee";
attrE.Type = typeof(Employee);
// Adds the element to the collection of elements.
attrs.XmlElements.Add(attr);
attrs.XmlElements.Add(attrE);
// Creates the XmlAttributeOverrides instance.
XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();
// Adds the type of the class that contains the overridden
// member, as well as the XmlAttributes instance to override it
// with, to the XmlAttributeOverrides.
attrOverrides.Add(typeof(Orders), "Persons", attrs);
// Creates the XmlSerializer using the XmlAttributeOverrides.
XmlSerializer s =
new XmlSerializer(typeof(Orders), attrOverrides);
During serialization, it will use override attributes to overrride their properties.
I've been trying to come up with a clean and reusable way to map entities to their DTOs. Here is an example of what I've come up with and where I'm stuck.
Entities
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public Address Address { get; set; }
// Other properties not included in DTO
}
public class Address
{
public int ID { get; set; }
public string City { get; set; }
// Other properties not included in DTO
}
DTOs
public class PersonDTO
{
public int ID { get; set; }
public string Name { get; set; }
public AddressDTO Address { get; set; }
}
public class AddressDTO
{
public int ID { get; set; }
public string City { get; set; }
}
Expressions
This is how I began to handle the mapping. I wanted a solution that wouldn't execute the query before mapping. I've been told that if you pass a Func<in, out> instead of Expression<Func<in, out>> that it will execute the query before mapping.
public static Expressions
{
public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = new AddressDTO()
{
ID = person.Address.ID,
City = person.Address.City
}
}
}
One issue with this is that I already have an expression that maps an Address to an AddressDTO so I have duplicated code. This will also break if person.Address is null. This gets messy very quick especially if I want to display other entities related to person in this same DTO. It becomes a birds nest of nested mappings.
I've tried the following but Linq doesn't know how to handle it.
public static Expressions
{
public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = Convert(person.Address)
}
public static AddressDTO Convert(Address source)
{
if (source == null) return null;
return new AddressDTO()
{
ID = source.ID,
City = source.City
}
}
}
Are there any elegant solutions that I'm missing?
If you want to create mappings manually then you can use Select on the collection in the following way:
Some test data:
var persons = new List<Person>
{
new Person() {ID = 1, Name = "name1", Address = new Address() {ID = 1, City = "city1"}},
new Person() {ID = 2, Name = "name2", Address = new Address() {ID = 2, City = "city2"}},
new Person() {ID = 3, Name = "name3", Address = new Address() {ID = 1, City = "city1"}}
};
Mapping methods:
public static PersonDTO ToPersonDTOMap(Person person)
{
return new PersonDTO()
{
ID = person.ID,
Name = person.Name,
Address = ToAddressDTOMap(person.Address)
};
}
public static AddressDTO ToAddressDTOMap(Address address)
{
return new AddressDTO()
{
ID = address.ID,
City = address.City
};
}
Actual usage:
var personsDTO = persons.Select(x => ToPersonDTOMap(x)).ToList();
Keep in mind that if this was a real query is would not get executed as long as it was IQueryable, it would be executed once you materialize it (using ToList() for example).
However, I would consider using some framework which could do it (the mappings) for you automatically (if your mapping are as simple as provided example(.
Just use AutoMapper.
Example:
Mapper.CreateMap<Address, AddressDTO>();
Mapper.CreateMap<Person, PersonDTO>();
Your query will execute when the mapping is performed but if there are fields in the entity that you're not interested use Project().To<> which is available both for NHibernate and EntityFramework. It will effectively do a select on the fields specified in the mapping configurations.
Automapper is the best way .
For me, I use this for simple objects only, but I don't recommend it
public static class ObjectMapper
{
public static T Map<T>(object objfrom, T objto)
{
var ToProperties = objto.GetType().GetProperties();
var FromProperties = objfrom.GetType().GetProperties();
ToProperties.ToList().ForEach(o =>
{
var fromp = FromProperties.FirstOrDefault(x => x.Name == o.Name && x.PropertyType == o.PropertyType);
if (fromp != null)
{
o.SetValue(objto, fromp.GetValue(objfrom));
}
});
return objto;
}
}
And I call it like that wherever I want
var myDTO= ObjectMapper.Map(MyObject, new MyObjectDTO());
You could either use AutoMapper or write extension methods like these:
public static class PersonMapper
{
public static PersonDTO ConvertToDTO(this Person person)
{
return new PersonDTO { ID = person.ID, Name = person.Name, Address = person.Address.ConvertToDTO() };
}
public static IEnumerable<PersonDTO> ConvertToDTO(this IEnumerable<Person> people)
{
return people.Select(person => person.ConvertToDTO());
}
}
public static class AddressMapper
{
public static AddressDTO ConvertToDTO(this Address address)
{
return new AddressDTO { ID = address.ID, City = address.City };
}
public static IEnumerable<AddressDTO> ConvertToDTO(this IEnumerable<Address> addresses)
{
return addresses.Select(address => address.ConvertToDTO());
}
}
You could then map a Person object to a PersonDTO object like this:
public class Program
{
static void Main(string[] args)
{
Person person = new Person { ID = 1, Name = "John", Address = new Address { ID = 1, City = "New Jersey" } };
PersonDTO personDTO = person.ConvertToDTO();
Console.WriteLine(personDTO.Name);
}
}
I see the way you want to do it. I can propose you this solution:
public class PersonDTO
{
public int ID { get; set; }
public string Name { get; set; }
public AddressDTO Address { get; set; }
public static Expression<Func<Entities.Person, PersonDTO>> PersonSelector
{
get
{
return person => new PersonDTO()
{
ID = x.Id,
Name = x.Name,
Address = x.Address
.Select(AddressDTO.AddressSelector)
};
}
}
}
public async Task<PersonDTO> GetPerson(int id)
{
var person = await _personRepository.Get(id, PersonDTO.PersonSelector);
return person;
}
public async Task<TResult> Get<TResult>(int id, Expression<Func<Person, TResult>> selector)
{
var result = await _context.Persons
.Where(x => x.Id == id)
.Select(selector)
.SingleOrDefaultAsync();
return result;
}
I need a way to get the values from a generic HashSet using reflection. Here is what I've tried (you can copy/paste this on a console app):
class Program
{
public class Order
{
public int Id { get; set; }
}
public class Person
{
public string Name { get; set; }
public ICollection<Order> Orders { get; set; }
}
static void Main(string[] args)
{
var person = new Person();
person.Name = "Test Person";
person.Orders = new HashSet<Order>();
person.Orders.Add(new Order() { Id = 1 });
person.Orders.Add(new Order() { Id = 2 });
var reflectedOrders = person.GetType().GetProperty("Orders").GetValue(person, null);
Console.WriteLine("How do I iterate the reflected orders?");
Console.ReadLine();
}
}
EDIT
It's an example, in the real application I don't know which type to convert the reflected Orders. I only know the property is an ICollection<T> (turned to HashShet by EF)
Did you tried casting reflectedOrders to IEnumerable?
IEnumerable reflectedOrders = (IEnumerable)person.GetType().GetProperty("Orders").GetValue(person, null);
It should be simple as casting:
var reflectedOrders = (HashSet<Order>) person.GetType().GetProperty("Orders").GetValue(person, null);
foreach (var order in reflectedOrders)
...
What about
var orders = persons.OfType<Person>().SelectMany(p => p.Orders);