I have 'extended' the System.DateTime struct by adding some essential fields to it. Ideally I'd like to be able to deliver this object via a webservice to a winforms client.
I've marked the stuct type as [Serializable] and it also implments ISerializable, however if I inspect the XML being delivered by the webservice it simply contains an empty tag for the object.
Putting breakpoints all over the place has lead me to believe that when the object gets de-hydrated the ISerializable method void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) never appears to get called.
There are various reasons why I'd prefer to keep this as a struct, but will convert it to a class if necessary.
Does anyone know why GetObjectData is being ignored by the .net framework while it is preparing the data for the webservice response? The struct which I am working with contains a DateTime member and a few booleans.
please note, this is .net 2.0!
Cheers
First, web-services use XmlSerializer - so you'd need IXmlSerializable for custom serialization. The standard XmlSerializer serialization only acts on public properties that have both a getter and setter.
Second, structs generally don't work very well as web-service DTO objects; in particular, XmlSerializer demands things be mutable... which structs shouldn't be.
I'd use a class, personally. If you can give more info, I might be able to say more...
For example:
[Serializable]
public class FunkyTime
{
[XmlAttribute]
public DateTime When { get; set; }
[XmlAttribute]
public bool IsStart { get; set; }
[XmlAttribute]
public bool IsEnd { get; set; }
}
(note you can tweak the xml layout / names in various ways)
Related
I'm considering migrating current WCF-based based application to protobuf-net.Grpc. It seems to be doable, however I was not able to make protobuf-net serialize properties of (DTO classes) base class without including all derived classes with [ProtoInclude] attribute.
Simplified class hierarchy:
[DataContract]
public abstract class DtoBase
{
[DataMember(Order = 1)]
public int Id { get;set; }
[DataMember(Order = 2)]
public int Version { get;set; }
[DataMember(Order = 3)]
public EditState EditState { get;set; }
}
[DataContract]
public class PersonDto : DtoBase
{
[DataMember(Order=4)]
public string FirstName { get;set; }
[DataMember(Order=5)]
public string LastName { get;set; }
}
I have investigated related questions and it all boils down to the fact that specific type should be known during deserialization - or there should be a way to determine it. Our service methods already know the specific subclass to use, e.g. we have methods like
[ServiceContract]
public interface IPersonService
{
[OperationContract]
ScalarResult<PersonDto> GetById(personId);
}
DataContractSerializer can do that - deserialize base class properties, when specific subclass is already known. It needs hints (known types) when you deserialize subclass having base class signature, like returning PersonDto instead of DtoBase. But when specific subclass is known, known types are not needed and everything just works.
So the question is how to do the same with protobuf-net? And if it's not possible, why?
Protobuf-net, like any library, makes certain assumptions and compromises. If it wants to support additional scenarios, they need to be specified, designed, implemented, tested and supported - all of which takes time. So far, the scenario you describe: hasn't had that time invested.
It may be possible to configure the base-type properties using the RuntimeTypeModel API, but I must emphasize: whenever a question arises that is essentially:
My existing model isn't working well with my chosen serializer
my default response (based on literally decades of experience in this field) is:
If your existing model isn't a great fit for a different serializer: stop fighting the serializer. Instead, create a new model that works perfectly with your new choice of serializer, and shim between models at the point of (de)serialization
This question already has answers here:
Is DataContract attributes required for WCF
(4 answers)
Closed 9 years ago.
I was wondering if there is any way to define a WCF Contract class without using the [DataContract] and [DataMember] annotation. The reason is that domain model we currently have is fairly clean so we would like to keep it this way. Whats the best practice here? Create a Transfer object and copy the domain model object into a transfer object (that has the required annotations and is the Contract transfered between Client and Server)? Or somehow not annotate the object model and specify the contract in a different way.
If you do not add any serialization attributes to your class, and use it as part of a WCF service contract method, WCF will use the default serialization rules to produce a data contract anyway. This means that the class will implicitly become a [DataContract] every public property that has both a get and set accessor will implicitly become a [DataMember].
The only time you need to apply the attributes is if you want to override the default behavior, e.g. hiding some attributes, applying namespaces, etc. It's generally considered good practice to do so anyway, because relying on the default behavior might get you in trouble later. (It also makes it explicit that your class is meant for use by WCF). But it's not strictly required, as long as the default behavior meets your needs.
In response to your follow-up:
As far as I know there's no completely external way to change the serialization behavior of the DataContractSerializer for a given class; every option requires at least some level of attribution on the class being serialized. As #Yair Nevet describes below, my preferred method for turning existing domain objects into data contracts is the MetadataType attribute.
Alternatively, you can bypass the whole issue by doing what you suggested in your question: don't serialize your domain objects, but create custom DTO objects and serialize them. I tend to do this whenever I'm using the Entity Framework, for example, because serializing those can be tricky. This is also a good approach to take if your domain objects have lots of behaviors built into them -- you get a clear separation of "data being passed around" vs. "objects participating in my business logic."
You often end up with lots of redundant code, but it does achieve your goal of zero changes to your existing objects.
You can use the MetadataType attribute and a metadata model class in order to separate the annotations from your model.
For example:
[MetadataType(typeof(MyModelMetadata))]
public class MyModel : MyModelBase {
... /* the current model code */
}
[DataContract]
public class MyModelMetadata {
[DataMember]
public string Name { get; set; }
}
WCF is capable of serializing your objects without the attributes. The attributes are there to allow for customization. For example, the two classes will serialize identically by the DataContractSerializer:
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
[DataContract]
public class Customer
{
[DataMember] public string FirstName { get; set; }
[DataMember] public string LastName { get; set; }
}
It is worth mentioning that you really should mark your class with the attributes. They aren't as "messy" as you think. It will actually save you from headache in the future. For example:
[DataContract(Name = "Customer")]
public class Customer
{
[DataMember(Name = "FirstName")]
public string FirstName { get; set; }
[DataMember(Name = "LastName")]
public string LastName { get; set; }
}
In the previous code sample, I explicitly set the names of the class and members. This will allow me to refactor the names without breaking consumers code. So, if someone decides that my class should be named CustomerDetail instead of Customer, I can still leave the name as Customer so that consumers of my service continue to work.
You could always use DTOs. Make a separate class that has everything that is needed to serialize your objects. Then project your domain model on to the DTO. You could use something like AutoMapper to make this process a little easier.
Regarding Performance
Unless you have hundreds, probably thousands, or objects or a very large number of properties per class, the act of converting to and from DTOs probably isn't that much performance overhead.
If you are using something like EF, and you are not serializing every property, you might even be able to reduce some overhead by projecting your EF query directly on to your DTOs.
This is kind of a dramatic case, but I had (poorly designed) database models with 50+ properties per type. By changing to DTOs that only have the 10-15 properties I cared about, I was able to almost double the performance of a WCF service.
This question already has answers here:
Can I optionally turn off the JsonIgnore attribute at runtime?
(3 answers)
Closed 4 years ago.
I am currently using the same C# DTOs to pull data out of CouchDB, via LoveSeat which I am going to return JSON via an ASP MVC controller.
I am using the NewtonSoft library to seralise my DTOs before sending them down through the controller.
However, as CouchDB also uses NewtonSoft it is also respecting the property level NewtonSoft attributes such as
[JsonIgnore]
[JsonProperty("foo")]
Is there anyway to tell the newtonsoft library to ignore these attributes explicitly? LoveSeat allows me to provide my own implementation of IObjectSerializer, which gives me full control over netwonsofts JsonSerializerSettings. So, can I ignore the attributes by using those settings ?
I ask as the only alternative I can see at this point, is to dupe my DTOs. While not that's not terrible, it isn't great either.
The only other way I can see is to bring in my own version of the Newtonsoft.Json source into my project, with a different assembly name etc etc. But this way madness definitely lies and I will just dupe the DTOs before I go down this road.
I'm not sure if this is what you're after, but from what I understand you're looking for the [JsonIgnore] attribute. Stops properties from being serialized with the rest of the object into to JSON.
[JsonIgnore]
public string Whatever{ get; set; }
One suggestion that you may not like. For best practices, I recommend having two almost identical objects. One specifically for your Data Access Layer (Domain Object) which maps to your DB. And a separate DTO that your apps care about. This way the Domain Object will mostly contain more properties than the DTO and you can separate the concerns.
According to Json.NET documentation
You can add method to your class: public bool ShouldSerialize_________(){...} and fill in the blank with the name of the property you don't want to serialize. If the method returns false, the property will be ignored.
The example from the documentation doesn't want to serialize an employee's manager if the manager is the same employee.
public class Employee
{
public string Name { get; set; }
public Employee Manager { get; set; }
public bool ShouldSerializeManager()
{
// don't serialize the Manager property if an employee is their own manager
return (Manager != this);
}
}
You could put some kind of inhibit setting on your class:
public class DTO
{
[JsonIgnore]
public bool IsWritingToDatabase { get; set; }
public string AlwaysSerialize { get; set; }
public string Optional { get; set; }
public bool ShouldSerializeOptional()
{
return IsWritingToDatabase;
}
}
But, this isn't much simpler than having two objects. So I would recommend doing as #zbugs says, and having separate definitions for API-side and DB-side.
I ended up making all properties I needed to only add attributes to virtual, and overriding them alone in another class, with the relevant newtonsoft attributes.
This allows me to have different serialisation behavior when de-serialising from CouchDB and serialising for a GET, without too much dupe. It is fine, and a bonus, that the two are coupled; any changes in the base i would want anyway.
It would still be nice to know if my original question is possible?
This newtonking.com link helped in a similar situation. It extends the DefaultContractResolver class. To make it work I had to replace
protected override IList<JsonProperty> CreateProperties(JsonObjectContract contract)
with
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
I imagine to use XML serialization like this:
class Foo {
public Foo (string name) {
Name1 = name;
Name2 = name;
}
[XmlInclude]
public string Name1 { get; private set; }
[XmlInclude]
private string Name2;
}
StreamWriter wr = new StreamWriter("path.xml");
new XmlSerializer<Foo>().Serialize (wr, new Foo ("me"));
Edit: I know this code is wrong. It was just to display how I would like to use it.
But this does not work at all:
XmlSerializer is not generic. I have to cast from and to object on (de)serialization.
Every property has to be fully public. Why aren't we just using Reflection to access private setters?
Private fields cannot be serialized. I'd like to decorate private fields with an attribute to have XmlSerializer include them.
Did I miss something and XmlSerializer is actually offering the described possibilities? Are there alternate serializers to XML that handle these cases more sophisticatedly?
If not: We're in 2010 after all, and .NET has been around for many years. XML serialization is often used, totally standard and should be really easy to perform. Or is my understanding possibly wrong and XML serialization ought not to expose the described features for a good reason?
Edit: Legacy is not a good reason imo. Listwas nongeneric at first, too.
(Feel free to adjust caption or tags. If this should be CW, please just drop a note.)
See XmlSerializer class. You'll see you're using it wrong. XmlInclude has a totally different purpose.
You're right. The XML Serializer has been around since .NET 1.0. That's before we had generics, BTW, so it's unlikely to support them.
Also, better technologies have arrived since then:
The DataContractSerializer is faster, and supports serializing as binary
LINQ to XML can be used in many serialization scenarios, and is much more flexible
The XML Serializer is unlikely to be enhanced in the future. I recommend you learn the other alternatives.
First the fixed code, then the answers to your questions:
public class Foo {
public Foo() : this("") {}
public Foo (string name) {
Name1 = name;
Name2 = name;
}
// note only this will be serialized
public string Name1 { get; private set; }
// this won't
private string Name2;
}
or in 3.0:
[DataContract]
class Foo {
public Foo (string name) {
Name1 = name;
Name2 = name;
}
[DataMember]
public string Name1 { get; private set; }
[DataMember]
private string Name2;
}
(and use DataContractSerializer instead of XmlSerializer)
XmlSerializer is not generic. I have to cast from and to object on (de)serialization.
That is common for serializers. I have my own serializer, and initially I did make it fully generic. And it turned out to be a big design mistake. Huge. No, seriously. I'm currently in the process of re-writing every line of code to switch it out.
Simply; serializers generally involve some level of reflection (either for code-gen or for the actual work, depending on the implementation). Reflection and generics don't play nicely, especially on some of the frameworks like WCF. Having your code do the final cast is a fair compromise. I have a number of blog entries on this if you really want...
Every property has to be fully public.
That is indeed a limitation of XmlSerializer (although a list/colletion without a setter is fine, it will throw if you have a public get and private set). Also, the type needs to be public and have a parameterless constructor.
Why aren't we just using Reflection to access private setters?
For performance, XmlSerializer builds an assembly on the fly to do what you want. It doesn't have automatic access to your code's internals. For info, I'm doing something similar but I offer 2 levels of generation; fully static (into a deployable dll), which then only works with public members, or in-memory, which can still access private members. I guess they wanted to settle on only 1 model, which makes sense - and they needed "sgen", which dictates the first model.
Private fields cannot be serialized. I'd like to decorate private fields with an attribute to have XmlSerializer include them.
Then use DataContractSerializer, which will serialize any member (including private) marked [DataMember].
1: legacy. XML Serializer predates generics. It as in with .NET 1.0.
2: Design decision. XML serializer is supposed to work with very limtied rights, compared to other solutions.
3: same as 2.
You can use WCF DataContract serializer in parts.
Your assumption is "limited wrong". XML serialization is supposedly for transfer documents, which - in my projects - are always separate classes doing nothing more. As such, I have no problem with all the limitations.
You don't need the
[XmlInclude]
Like you have it. You can use
[XmlElement]
[XmlAttribute]
...
To describe the way the class is serialized.
Take out the [XmlInclude] and see if that works.
class Foo {
public Foo (string name) {
Name1 = name;
Name2 = name;
}
[XmlAttribute]
public string Name1 { get; set; }
[XmlAttribute]
public string Name2;
}
Foo myFoo = new Foo("FirstName", "LastName");
StreamWriter wr = new StreamWriter("path.xml");
XmlSerializer serializer = new XmlSerializer(typeof(Foo));
serializer.Serialize(wr, myFoo);
Updated, the serialized properties should be public.
By the way DataContract never supports binary serialization , it serializes to xml but supports binary encoding.
I have some simple POCO object:
public class ProductCategoryDTO
{
public string Name { get; set; }
public DateTime ModifiedDate { get; set; }
}
As sometimes field order is important (for example, if sending to Infopath forms), I need to keep element order when serializing.
And now I am confused, what attributes I should use for the class and for each field. I know that:
DataContractSerializer uses [DataContract] and [DataMember(Order = n)]
XMLSerializer uses [Serializable] and [XmlElementAttribute(Order = n)].
Then what attributes to use if I want to support both XMLSerializer and DataContractSerializer, so it can used in both WCF or ASP. web services?
Strictly speaking, you don't need to use any attributes for either ;-p It used to be that DataContractSerializer would demand [DataContract] / [DataMember] (and they absolutely should be used), but you can use it without (but it then acts in a very dubious way similar to BinaryFormatter). Likewise, XmlSerializer doesn't need anything unless you want to control things. There are, however, some differences you should note:
XmlSerializer demands (and uses) a public parameterless constructor; DataContractSerializer doesn't use a constructor (at all). So watch out for that, and don't (for WCF) rely on code in the ctor - if you have necessary init code, use a serialization callback for WCF.
XmlSerializer demands either public fields (yeuch) or public properties with both get and set (even for lists); DataContractSerializer will happily work against private members, properties with (for example) a public get and private set, and collections without a `set (as long as your type initialises it).
XmlSerializer demands public types; IIRC DataContractSerializer is less fussy
So yes; you can support both serializers, and you can add any number of attributes in parallel, but note the above if you want total compatibility.
Another option is to just use XmlSerializer; you can configure WCF to use XmlSerializer by using [XmlSerialzerFormat]. Both options support inheritance, via [XmlInclude] and [KnownType].
Finally, note that if you implement IXmlSerializable, this takes precedence over either, but it hard to get right. Don't do that unless you have to.
I don't see any reason why you couldn't put both attributes on the class and member properties, if you really must. Doesn't look nice, but if it works for you, that's just fine!
[DataContract(Namespace="....")]
[XmlType]
public class ProductCategoryDTO
{
[DataMember(Order=1)]
[XmlElementAttribute(Order=1)]
public string Name { get; set; }
[DataMember(Order=2)]
[XmlElementAttribute(Order=2)]
public DateTime ModifiedDate { get; set; }
}
Order of XML elements should be dictated by the WSDL and you don't need to worry about it. Starting from .NET 3.5 SP1 you no longer need to use DataContractAttribute and DataMemberAttribute. The serializer will automatically include all public properties. As far as XmlSerializer is concerned, the SerializableAttribute has no effect. This attribute is used for binary serialization by the BinaryFormatter. So to resume, you could leave the class as a POCO, expose it either in WCF or ASP.NET webservice and leave the clients consume it according to the WSDL.