Why would a WCF DataContract not serialize members in alphabetical order? - c#

I have several DataContracts that looks similar to this (shortened for brevity):
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
[DataMember(IsRequired = true)]
public string Name { get; set; }
[DataMember]
public string Value { get; set; }
[DataMember(IsRequired = true)]
public int Id { get; set; }
public ExtensionDataObject ExtensionData { get; set; }
}
I hadn't taken notice of the serialized messages before but after a recent change, two things were done: I added a new property, called ReturnCode, and ran CodeMaid's "Reorganize", which alphabetized the properties.
It now looked something like this:
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
public ExtensionDataObject ExtensionData { get; set; }
[DataMember(IsRequired = true)]
public int Id { get; set; }
[DataMember(IsRequired = true)]
public string Name { get; set; }
[DataMember]
public int ReturnCode { get; set; }
[DataMember]
public string Value { get; set; }
}
According to Microsoft's page on Data Contract Member Order I realized ReturnCode would break the contract since the serializer would insert it before Value, so I added an Order attribute value, assuming the original order was alphabetic, yielding:
[DataContract(Name = "ItemDTO", Namespace = "http://foo.com/")]
public class ItemDTO : IExtensibleDataObject
{
public ExtensionDataObject ExtensionData { get; set; }
[DataMember(IsRequired = true, Order = 0)]
public int Id { get; set; }
[DataMember(IsRequired = true, Order = 1)]
public string Name { get; set; }
[DataMember(Order = 3)]
public int ReturnCode { get; set; }
[DataMember(Order = 2)]
public string Value { get; set; }
}
This however threw an exception that the deserialized members were out of order. I rolled back to a prior changeset, before all the changes, and sure enough the original order of the members was not alphabetic in the SOAP request (viewed through Fiddler), but following the original order expressed in the code, ie: Name, Value, Id.
I'm currently in the process of adding Order values to all my old DTO types to sequence them according to their prior, pre-alphabetizing of the properties, arrangement. What I'd like to know is why the coded order instead of alphabetized order was being used by the serializer? Microsoft's rules say:
Next in order are the current type’s data members that do not have the
Order property of the DataMemberAttribute attribute set, in
alphabetical order.
Update:
After I added the Order values to sequence the properties in their original order, I again ran Fiddler and it's still using the order the items are literally coded in. In other words, for some reason, my WCF service is completely ignoring any serialization sequencing logic and just sequencing the properties by the order they appear in the .cs file. In fact, the only way I was able to get it to serialize properly was to physically rearrange the properties in each type to their original order. That worked, but it's not preferred.
Update 2 - Solution:
Following Dracor's advice, I added [XmlElement(Order = 1)] attributes and an XmlRootAttribute to my DTOs. The SOAP serialization DID end up following the ordering assigned by these attributes. I hadn't considered it but my service does use Castle DynamicProxy and so I'm guessing it's changing the serializer from DataContractSerializer to XmlSerializer.

Why don't you simply use XmlSerializer to Serialize/Deserialize your XML? It's way more forgiving than DataContractSerializer, and works most of the time.

Related

System.Json - custom rules for property serialization skipping

I am trying to migrate from Newtonsoft.Json to System.Text.Json
However, I ran into a problem since I was using DefaultContractResolver.
My "custom" behaviour have these rules for property serialization:
Skip property serialization if it is marked with ReadOnly attribute
Skip property serialization in case of null (this is supported)
Skip property serialization which would serialize into an empty object
Example:
class Car
{
[ReadOnly]
public string Id { get; set; }
public string Name { get; set; }
public Person Owner { get; set; }
}
class Person
{
[ReadOnly]
public string Id { get; set; }
public string Name { get; set; }
}
Now, imagine, we have this data if no rules would apply.
{
"Id":"1234",
"Name":"Skoda",
"Owner":{
"Id":"abcd",
"Name":null
}
}
Now, if I serialize the object, I would like to get this instead.
{
"Name":"Skoda"
}
In order to ignore individual properties, you need to use the [JsonIgnore] attribute along with one of these conditions:
Always;
Never;
WhenWritingDefault;
WhenWritingNull.
You can also define a default ignore condition through the JsonSerializerOptions type.
If additional behavior is needed, you should write a custom converter.
Example:
class Person
{
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
public string Id { get; set; }
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string Name { get; set; }
}
More information:
How to ignore properties with System.Text.Json
How to write custom converters for JSON serialization (marshalling) in .NET

Protobuf-net error with nested classes: 'Type is not expected, and no contract can be inferred: ...'

I am using Protobuf-net v2.4.4 to serialize a series of nested classes that contain large double arrays. Besides the custom classes, all data types are primitives or arrays/lists of primitives such as strings or double[] and I am not sure why I am getting this error. The error also seems to randomly pop up for my classes. A class serializes fine - after a restart, it stops working and throws the above error. I also was able to serialize one class in a new project but not in my original project.
Is there any good way to debug this and to find the source of it? I suspect it is some kind of project setting or conflicting DLLs since the new project obviously has a lot fewer references.
I began serializing each custom class on its own in order to track down the problem but I am not sure if this is reliable given the experience mentioned above. Nevertheless, I am pasting a simplified example of my code below. Classes with the issue are MyClass2 -> MyClass5 and MyClass3. But then I cannot see anything out of the ordinary in these classes.
Any suggestions would be greatly appreciated.
[ProtoContract] public class MyParentClass
{
[DataMember] [ProtoMember(1)] public MyClass1 Settings { get; set; } // On its own this class serializes fine
[DataMember] [ProtoMember(2, OverwriteList = true)] public List<MyClass2> Zones { get; set; } = new List<MyClass2>(); // ProtoBuf does not like this class
...
[DataMember] [ProtoMember(4)] public MyClass3 Results { get; set; } // This serializes fine in one project but not in conjunction with the rest here
}
[ProtoContract] public class MyClass2
{
[DataMember] [ProtoMember(5)] public MyClass4 Settings { get; set; } // On its own this class serializes fine
[DataMember] [ProtoMember(6, OverwriteList = true)] public List<MyClass5> faces { get; set; } = new List<MyClass5>(); // ProtoBuf does not like this one
[DataMember] [ProtoMember(7)] public MyClass6 Result { get; set; } // This class looks similar to MyClass3
...
}
[ProtoContract] public class MyClass5
{
[DataMember] [ProtoMember(4)] public Enum1 Bcond { get; set; } = Enum1._UNSET_; // These enums are also decorated with ProtoContract and ProtoEnum
...
[DataMember] [ProtoMember(7)] public string Twin { get; set; } = "";
...
}
[ProtoContract] public class MyClass3
{
[DataMember] [ProtoMember(1)] public double energy { get; set; }
...
[DataMember] [ProtoMember(3, OverwriteList = true)] public double[] MoreEnergy { get; set; } = new double[8760];
...
[DataMember] [ProtoMember(17, OverwriteList = true)] public double[] EvenMoreDataButNotSetAutomatically { get; set; }
}
Fundamentally, I can't make it repro what you're seeing; taking the code above and playing with it locally, it just works.
However! There are some potentially odd timing scenarios if you start serializing types concurrently - typical on web-servers etc. A good fix here would be to make sure that all the prep work happens as early as possible, for example in your startup code you could do:
Serializer.PrepareSerializer<MyParentClass>();
(and perhaps a few others)
Note that the new double[8760] is a bad idea here, as protobuf-net will run the constructor and then swap the array; you might want to defer that and create it in your own code (or there are various other tricks that can be used). You may also want to use "packed" arrays for this one, i.e.
[ProtoMember(3, OverwriteList = true, IsPacked = true)]
on the MoreEnergy property.
In v3 "packed" encoding is enabled by default when applicable, so it is essentially opt-out; in v2.* it is opt-in. So: best to be explicit.

How to modify "Order" of "DataMember" attribute at runtime? [duplicate]

This question already has answers here:
Change custom attribute's parameter at runtime
(2 answers)
Closed 3 years ago.
I have a datamembers where order values are mentioned. i want to modify the value according to the order of parameters present in select query. I am unable to set the order value for the datamember at runtime.
Below is the code i tried :
[DataContract]
public class Details
{
[DataMember(EmitDefaultValue = false, Order = 1)]
public string id;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string name;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string creator;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string format;
[DataMember(EmitDefaultValue = false, Order = 1)]
public string creationTime;
}
Type type = executing.GetType("Details");
FieldInfo[] properties = type.GetFields();
properties[0].GetCustomAttributes(typeof(DataMemberAttribute), true).SetValue(2, 3);
I tried the above code to get custom attribute and set value, but its not working.
Is it possible to change attribute values during runtime?
Unfortunately, there is no way to change the value of the Order parameter at runtime. Attributes are already evaluated and integrated in the compiled code at build time so that you can only provide constant values.
The most generic way would be to change the serialization code so that the properties are serialized in the requested order, but this is way too much effort and risk for just adjusting the order, at least imho.
However, what you could do if you have a very limited set of possible queries to create separate methods for each variation and have different classes for the return value that adjust the values as required. One possible approach would be to create a base class for all objects. First, you'd have to use properties instead of fields in your base class. In addition, the keyword virtual prepares the properties for being overriden in a derived class:
[DataContract]
public class Details
{
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string id { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string name { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string creator { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string format { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public virtual string creationTime { get; set; }
}
For each variant, you'd create a derived class, like:
[DataContract]
public class DetailsVariantA : Details
{
[DataMember(EmitDefaultValue = false, Order = 5)]
public override string id { get; set; }
[DataMember(EmitDefaultValue = false, Order = 4)]
public override string name { get; set; }
[DataMember(EmitDefaultValue = false, Order = 3)]
public override string creator { get; set; }
[DataMember(EmitDefaultValue = false, Order = 2)]
public override string format { get; set; }
[DataMember(EmitDefaultValue = false, Order = 1)]
public override string creationTime { get; set; }
}
The method for variant A would return an object of type DetailsVariantA instead of Details.
A word of caution: as you can see in the sample, this approach also involves a lot of extra code because you'd have another class per variant. Also, it introduces the risk that later on, someone forgets to add the properties to all derived classes and so on.
As WCF is primarily used to exchange data between machines and the data is usually not read by humans, from my point of view, I'd not invest the effort and introduce this risk for just changing the order.

Resharper: Don't change order of member variables in JSON classes

The order of the members can be set up in the ReSharper options in Languages, C#, Type Members Layout. Resharper is doing it correctly. However, I would like to exclude certain classes which contain JSONProperty attribute.
So for example, refer class below. I don't want Resharper to reorder the members in it.
internal class ExecutionParametersJson
{
[JsonProperty("Factor")]
public string Factor { get; set; }
[JsonProperty("Penalty")]
public string Penalty { get; set; }
[JsonProperty("Origin")]
public string Origin { get; set; }
[JsonProperty("InterFactor")]
public string InterFactor { get; set; }
}
I am using latest version of Resharper.
Can anyone show me how to configure Resharper to achieve this?
Actually, I tried putting Order attribute in it. But that didn't do any difference.
[JsonProperty("Factor", Order = 1)]
public string Factor{ get; set; }
However, I would like to exclude certain classes which contain JSONProperty attribute.
Yes that can be done rather easily.
Given this exammple code, note the additional properties I included purely to prove a later point:
internal class ExecutionParametersJson
{
[JsonProperty("Factor")]
public string Factor { get; set; }
public string SomeProperty { get; set; }
[JsonProperty("Penalty")]
public string Penalty { get; set; }
[JsonProperty("Origin")]
public string Origin { get; set; }
public int SomeOtherProperty { get; set; }
[JsonProperty("InterFactor")]
public string InterFactor { get; set; }
}
...then choose Resharper.Options.Code Editing.c#.File Layout, the list of patterns appears:
Choose your preferred pattern. Here I chose Default Pattern. I've been adding to it in the past so it may look different.
Scroll down till you find a region for Properties, you may have to create it like so:
Select Properties, Indexers, ensure Sort By is set to Name.
Double-click Properties, Indexers. The conditions editor appears. Add a top-level And condition; Not and specify the JsonProperty.
Now run Resharper.Edit.Cleanup Code on the file in question. All properties, except those with a JsonProperty attribute, will be sorted alphabetically and placed into a region titled Properties.
internal class ExecutionParametersJson
{
#region Properties
public int SomeOtherProperty { get; set; }
public string SomeProperty { get; set; }
#endregion
[JsonProperty("Factor")]
public string Factor { get; set; }
[JsonProperty("Penalty")]
public string Penalty { get; set; }
[JsonProperty("Origin")]
public string Origin { get; set; }
[JsonProperty("InterFactor")]
public string InterFactor { get; set; }
}
Now the additional properties I included here was just to prove how you format members conditionally. Feel free to remove these properties; the #region or customise to your liking.
Moving on
You may want to tidy this up a bit and create a specific pattern in Resharper called JSON Classes or some such.

Using Datacontract: WCF

How should i be declaring the datacontracts
My Operation contract has a Method:
Apple GetApples()
My data Contract Apple looks Like
[DataContract]
public class Apple
{
[DataMember]
public int Id { get; set; }
[DataMember]
public FruitType type { get; set; }
}
As there is another member of type FruitType.
[DataContract]
public class FruitType
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string type { get; set; }
}
OR
as a simple class
public class FruitType
{
public int Id { get; set; }
public string type { get; set; }
}
What is the difference between these two? other than that the simple type is not a datacontract and will depende on how i want to use it.?
how should i declare it??
Those attributes give you the control over how your properties will be represented in different formats. For example for XML you can specify the XML Namespace and XML node names.
Even if you are happy with default property names and default namespace, when you try to serialize data to XML, your XML nodes will have weird names such as typek_BackingField.
In other words, if you use WCF you should use DataContract and DataMember attributes, even if you think it works fine the formatted data may not look what you expect. As a result it removes compatibility with other (non-WCF) systems. Or even when you don't share your types (contracts) with other WCF systems.

Categories