Ignore XML element order during deserialization [duplicate] - c#

I'm trying to implement a client for a service with a really deficient spec. It's SOAP-like, although it has no WSDL or equivalent file. The spec also doesn't provide any information about the correct ordering of elements - they're listed alphabetically in the spec, but the service returns an XML parse error if they're out of order in the request (said order to be derived by examining the examples).
I can work with this for submitting requests, even if it's a pain. However, I don't know how to handle responses correctly.
With both SoapEnvelope and directly with XmlSerializer, if the response contains an element I haven't yet ordered correctly, it shows up as null on my object. Once again, I can manage to work with this, and manually order the class properties with Order attributes, but I have no way to tell whether the original XML has a field that I didn't order correctly and thus got left as null.
This leads me to the current question: How can I check if the XmlSerializer dropped a field?

You can use the XmlSerializer.UnknownElement event on XmlSerializer to capture out-of-order elements. This will allow you to manually find and fix problems in deserialization.
A more complex answer would be to correctly order your elements when serializing, but ignore order when deserializing. This requires using the XmlAttributes class and the XmlSerializer(Type, XmlAttributeOverrides) constructor. Note that serializers constructed in this manner must be cached in a hash table and resused to avoid a severe memory leak, and thus this solution is a little "finicky" since Microsoft doesn't provide a meaningful GetHashCode() for XmlAttributeOverrides. The following is one possible implementation which depends upon knowing in advance all types that need their XmlElementAttribute.Order and XmlArrayAttribute.Order properties ignored, thus avoiding the need to create a complex custom hashing method:
// https://stackoverflow.com/questions/33506708/deserializing-xml-with-unknown-element-order
public class XmlSerializerFactory : XmlOrderFreeSerializerFactory
{
static readonly XmlSerializerFactory instance;
// Use a static constructor for lazy initialization.
private XmlSerializerFactory()
: base(new[] { typeof(Type2), typeof(Type1), typeof(TestClass), typeof(Type3) }) // These are the types in your client for which Order needs to be ignored whend deserializing
{
}
static XmlSerializerFactory()
{
instance = new XmlSerializerFactory();
}
public static XmlSerializerFactory Instance { get { return instance; } }
}
public abstract class XmlOrderFreeSerializerFactory
{
readonly XmlAttributeOverrides overrides;
readonly object locker = new object();
readonly Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>();
static void AddOverrideAttributes(Type type, XmlAttributeOverrides overrides)
{
if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string))
return;
var mask = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;
foreach (var member in type.GetProperties(mask).Cast<MemberInfo>().Union(type.GetFields(mask)))
{
XmlAttributes overrideAttr = null;
foreach (var attr in member.GetCustomAttributes<XmlElementAttribute>())
{
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlElements.Add(new XmlElementAttribute { DataType = attr.DataType, ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace, Type = attr.Type });
}
foreach (var attr in member.GetCustomAttributes<XmlArrayAttribute>())
{
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlArray = new XmlArrayAttribute { ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace };
}
foreach (var attr in member.GetCustomAttributes<XmlArrayItemAttribute>())
{
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlArrayItems.Add(attr);
}
foreach (var attr in member.GetCustomAttributes<XmlAnyElementAttribute>())
{
overrideAttr = overrideAttr ?? new XmlAttributes();
overrideAttr.XmlAnyElements.Add(new XmlAnyElementAttribute { Name = attr.Name, Namespace = attr.Namespace });
}
if (overrideAttr != null)
overrides.Add(type, member.Name, overrideAttr);
}
}
protected XmlOrderFreeSerializerFactory(IEnumerable<Type> types)
{
overrides = new XmlAttributeOverrides();
foreach (var type in types.SelectMany(t => t.BaseTypesAndSelf()).Distinct())
{
AddOverrideAttributes(type, overrides);
}
}
public XmlSerializer GetSerializer(Type type)
{
if (type == null)
throw new ArgumentNullException("type");
lock (locker)
{
XmlSerializer serializer;
if (!serializers.TryGetValue(type, out serializer))
serializers[type] = serializer = new XmlSerializer(type, overrides);
return serializer;
}
}
}
public static class TypeExtensions
{
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType;
}
}
}
Then when deserializing a type, use the XmlSerializer provided by the factory. Given that SoapEnvelope is a subclass of XmlDocument, you should be able to deserialize the body node along the lines of the answer in Deserialize object property with StringReader vs XmlNodeReader.
Note -- only moderately tested. Demo fiddle here.

Related

Specify output format of DataContractJsonSerializer?

For the project at hand I have to use DataContractJsonSerializer for serialization and have to generate a specific output based on the member's values.
My class looks similar to this:
public class MyClass
{
public string foo;
public string bar;
public MyClass(string f, string b = "")
{
this.foo = f;
this.bar = b;
}
}
Now serialization of a list like
var list = new List<MyClass>
{
new MyClass("foo", "bar"),
new MyClass("foo1"),
new MyClass("foo2", "bar2")
};
should look like this
[{"foo": "bar"}, "foo1", {"foo2": "bar2"}]
or - better yet - escaped and as a string:
"[{\"foo\": \"bar\"}, \"foo1\", {\"foo2\": \"bar2\"}]"
A mixture of strings and objects. How could I achieve this?
I tried to override the ToString() method and serializing the corresponding strings resulting in unnecessarily escaped symbols, e.g. bar2 could be m/s and was escaped as m\\/s which could not be deserialized correctly on the web server.
Finally, I just need to serialize to this format. There is no need to deserialize this format with DataContractJsonSerializer.
What you would like to do is to conditionally replace instances of MyClass with a serialization surrogate that is a string or a dictionary, however using a primitive as a surrogate is not supported by data contract serialization, as explained here by Microsoft.
However, since you only need to serialize and not deserialize, you can get the output you need by manually replacing your List<MyClass> with a surrogate List<object> in which instances of MyClass are replaced with a string when bar is empty, and a Dictionary<string, string> otherwise. Then manually construct a DataContractJsonSerializer with the following values in DataContractJsonSerializerSettings:
Set KnownTypes to be a list of the actual types in the surrogate object list.
Set EmitTypeInformation to EmitTypeInformation.Never. This suppresses inclusion of type hints in the output JSON.
Set UseSimpleDictionaryFormat to true.
(Note that DataContractJsonSerializerSettings, EmitTypeInformation and UseSimpleDictionaryFormat are all new to .NET 4.5.)
Thus you could define your MyType as follows:
public interface IHasSerializationSurrogate
{
object ToSerializationSurrogate();
}
public class MyClass : IHasSerializationSurrogate
{
public string foo;
public string bar;
// If you're not going to mark MyClass with data contract attributes, DataContractJsonSerializer
// requires a default constructor. It can be private.
MyClass() : this("", "") { }
public MyClass(string f, string b = "")
{
this.foo = f;
this.bar = b;
}
public object ToSerializationSurrogate()
{
if (string.IsNullOrEmpty(bar))
return foo;
return new Dictionary<string, string> { { foo, bar } };
}
}
Then introduce the following extension methods:
public static partial class DataContractJsonSerializerHelper
{
public static string SerializeJsonSurrogateCollection<T>(this IEnumerable<T> collection) where T : IHasSerializationSurrogate
{
if (collection == null)
throw new ArgumentNullException();
var surrogate = collection.Select(i => i == null ? null : i.ToSerializationSurrogate()).ToList();
var settings = new DataContractJsonSerializerSettings
{
EmitTypeInformation = EmitTypeInformation.Never,
KnownTypes = surrogate.Where(s => s != null).Select(s => s.GetType()).Distinct().ToList(),
UseSimpleDictionaryFormat = true,
};
return DataContractJsonSerializerHelper.SerializeJson(surrogate, settings);
}
public static string SerializeJson<T>(this T obj, DataContractJsonSerializerSettings settings)
{
var type = obj == null ? typeof(T) : obj.GetType();
var serializer = new DataContractJsonSerializer(type, settings);
return SerializeJson<T>(obj, serializer);
}
public static string SerializeJson<T>(this T obj, DataContractJsonSerializer serializer = null)
{
serializer = serializer ?? new DataContractJsonSerializer(obj == null ? typeof(T) : obj.GetType());
using (var memory = new MemoryStream())
{
serializer.WriteObject(memory, obj);
memory.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(memory))
{
return reader.ReadToEnd();
}
}
}
}
And serialize your list to JSON manually as follows:
var json = list.SerializeJsonSurrogateCollection();
With the following result:
[{"foo":"bar"},"foo1",null,{"foo2":"bar2"}]
If you really need the string to be escaped (why?) you can always serialize the resulting string to JSON a second time with DataContractJsonSerializer producing a double-serialized result:
var jsonOfJson = json.SerializeJson();
Resulting in
"[{\"foo\":\"bar\"},\"foo1\",{\"foo2\":\"bar2\"}]"
Deserialize will happen based on name and value combination so your text should be like this
[{"foo": "foo","bar": "bar"},{"foo": "foo1"},{"foo": "foo2","bar": "bar2"}]

Why Json.NET can't deserialize structs without JsonConstructor attribute? [duplicate]

I have a class that has a default constructor and also an overloaded constructor that takes in a set of parameters. These parameters match to fields on the object and are assigned on construction. At this point i need the default constructor for other purposes so i would like to keep it if i can.
My Problem: If I remove the default constructor and pass in the JSON string, the object deserializes correctly and passes in the constructor parameters without any issues. I end up getting back the object populated the way I would expect. However, as soon as I add the default constructor into the object, when i call JsonConvert.DeserializeObject<Result>(jsontext) the properties are no longer populated.
At this point I have tried adding new JsonSerializerSettings(){CheckAdditionalContent = true} to the deserialization call. That did not do anything.
Another note: the constructor parameters do match the names of the fields exactly except that the parameters are start with a lowercase letter. I wouldn't think this would matter since, like i mentioned, the deserialization works fine with no default constructor.
Here is a sample of my constructors:
public Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Json.Net prefers to use the default (parameterless) constructor on an object if there is one. If there are multiple constructors and you want Json.Net to use a non-default one, then you can add the [JsonConstructor] attribute to the constructor that you want Json.Net to call.
[JsonConstructor]
public Result(int? code, string format, Dictionary<string, string> details = null)
{
...
}
It is important that the constructor parameter names match the corresponding property names of the JSON object (ignoring case) for this to work correctly. You do not necessarily have to have a constructor parameter for every property of the object, however. For those JSON object properties that are not covered by the constructor parameters, Json.Net will try to use the public property accessors (or properties/fields marked with [JsonProperty]) to populate the object after constructing it.
If you do not want to add attributes to your class or don't otherwise control the source code for the class you are trying to deserialize, then another alternative is to create a custom JsonConverter to instantiate and populate your object. For example:
class ResultConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Result));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// Load the JSON for the Result into a JObject
JObject jo = JObject.Load(reader);
// Read the properties which will be used as constructor parameters
int? code = (int?)jo["Code"];
string format = (string)jo["Format"];
// Construct the Result object using the non-default constructor
Result result = new Result(code, format);
// (If anything else needs to be populated on the result object, do that here)
// Return the result
return result;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, add the converter to your serializer settings, and use the settings when you deserialize:
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new ResultConverter());
Result result = JsonConvert.DeserializeObject<Result>(jsontext, settings);
A bit late and not exactly suited here, but I'm gonna add my solution here, because my question had been closed as a duplicate of this one, and because this solution is completely different.
I needed a general way to instruct Json.NET to prefer the most specific constructor for a user defined struct type, so I can omit the JsonConstructor attributes which would add a dependency to the project where each such struct is defined.
I've reverse engineered a bit and implemented a custom contract resolver where I've overridden the CreateObjectContract method to add my custom creation logic.
public class CustomContractResolver : DefaultContractResolver {
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var c = base.CreateObjectContract(objectType);
if (!IsCustomStruct(objectType)) return c;
IList<ConstructorInfo> list = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).OrderBy(e => e.GetParameters().Length).ToList();
var mostSpecific = list.LastOrDefault();
if (mostSpecific != null)
{
c.OverrideCreator = CreateParameterizedConstructor(mostSpecific);
c.CreatorParameters.AddRange(CreateConstructorParameters(mostSpecific, c.Properties));
}
return c;
}
protected virtual bool IsCustomStruct(Type objectType)
{
return objectType.IsValueType && !objectType.IsPrimitive && !objectType.IsEnum && !objectType.Namespace.IsNullOrEmpty() && !objectType.Namespace.StartsWith("System.");
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
method.ThrowIfNull("method");
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
I'm using it like this.
public struct Test {
public readonly int A;
public readonly string B;
public Test(int a, string b) {
A = a;
B = b;
}
}
var json = JsonConvert.SerializeObject(new Test(1, "Test"), new JsonSerializerSettings {
ContractResolver = new CustomContractResolver()
});
var t = JsonConvert.DeserializeObject<Test>(json);
t.A.ShouldEqual(1);
t.B.ShouldEqual("Test");
Based on some of the answers here, I have written a CustomConstructorResolver for use in a current project, and I thought it might help somebody else.
It supports the following resolution mechanisms, all configurable:
Select a single private constructor so you can define one private constructor without having to mark it with an attribute.
Select the most specific private constructor so you can have multiple overloads, still without having to use attributes.
Select the constructor marked with an attribute of a specific name - like the default resolver, but without a dependency on the Json.Net package because you need to reference Newtonsoft.Json.JsonConstructorAttribute.
public class CustomConstructorResolver : DefaultContractResolver
{
public string ConstructorAttributeName { get; set; } = "JsonConstructorAttribute";
public bool IgnoreAttributeConstructor { get; set; } = false;
public bool IgnoreSinglePrivateConstructor { get; set; } = false;
public bool IgnoreMostSpecificConstructor { get; set; } = false;
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var contract = base.CreateObjectContract(objectType);
// Use default contract for non-object types.
if (objectType.IsPrimitive || objectType.IsEnum) return contract;
// Look for constructor with attribute first, then single private, then most specific.
var overrideConstructor =
(this.IgnoreAttributeConstructor ? null : GetAttributeConstructor(objectType))
?? (this.IgnoreSinglePrivateConstructor ? null : GetSinglePrivateConstructor(objectType))
?? (this.IgnoreMostSpecificConstructor ? null : GetMostSpecificConstructor(objectType));
// Set override constructor if found, otherwise use default contract.
if (overrideConstructor != null)
{
SetOverrideCreator(contract, overrideConstructor);
}
return contract;
}
private void SetOverrideCreator(JsonObjectContract contract, ConstructorInfo attributeConstructor)
{
contract.OverrideCreator = CreateParameterizedConstructor(attributeConstructor);
contract.CreatorParameters.Clear();
foreach (var constructorParameter in base.CreateConstructorParameters(attributeConstructor, contract.Properties))
{
contract.CreatorParameters.Add(constructorParameter);
}
}
private ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
protected virtual ConstructorInfo GetAttributeConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(c => c.GetCustomAttributes().Any(a => a.GetType().Name == this.ConstructorAttributeName)).ToList();
if (constructors.Count == 1) return constructors[0];
if (constructors.Count > 1)
throw new JsonException($"Multiple constructors with a {this.ConstructorAttributeName}.");
return null;
}
protected virtual ConstructorInfo GetSinglePrivateConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
return constructors.Length == 1 ? constructors[0] : null;
}
protected virtual ConstructorInfo GetMostSpecificConstructor(Type objectType)
{
var constructors = objectType
.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.OrderBy(e => e.GetParameters().Length);
var mostSpecific = constructors.LastOrDefault();
return mostSpecific;
}
}
Here is the complete version with XML documentation as a gist:
https://gist.github.com/bjorn-jarisch/80f77f4b6bdce3b434b0f7a1d06baa95
Feedback appreciated.
The default behaviour of Newtonsoft.Json is going to find the public constructors. If your default constructor is only used in containing class or the same assembly, you can reduce the access level to protected or internal so that Newtonsoft.Json will pick your desired public constructor.
Admittedly, this solution is rather very limited to specific cases.
internal Result() { }
public Result(int? code, string format, Dictionary<string, string> details = null)
{
Code = code ?? ERROR_CODE;
Format = format;
if (details == null)
Details = new Dictionary<string, string>();
else
Details = details;
}
Based on the answer by Zoltan, I created a variation that lets you use a specific constructor based on its signature.
Usage
return new JsonSerializerSettings
{
ContractResolver = new DynamicObjectResolver(t =>
{
if (t == typeof(QueueProperties))
return new Type[] { typeof(string) };
return null;
})
};
An here is the implementation
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Concurrent;
using System.Reflection;
namespace ConsoleApp76.Json
{
class DynamicObjectResolver : DefaultContractResolver
{
private readonly Func<Type, Type[]> GetConstructorSignature;
private readonly ConcurrentDictionary<Type, ConstructorInfo> TypeToConstructorLookup =
new ConcurrentDictionary<Type, ConstructorInfo>();
public DynamicObjectResolver(Func<Type, Type[]> getConstructorSignature)
{
if (getConstructorSignature is null)
throw new ArgumentNullException(nameof(getConstructorSignature));
GetConstructorSignature = getConstructorSignature;
}
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
var result = base.CreateObjectContract(objectType);
ConstructorInfo constructor = TypeToConstructorLookup.GetOrAdd(objectType, t => FindConstructorInfo(t));
if (constructor is null)
return result;
result.OverrideCreator = CreateParameterizedConstructor(constructor);
foreach (var param in CreateConstructorParameters(constructor, result.Properties))
result.CreatorParameters.Add(param);
return result;
}
private ConstructorInfo FindConstructorInfo(Type objectType)
{
Type[] constructorSignature = GetConstructorSignature(objectType);
if (constructorSignature is null)
return null;
return objectType.GetConstructor(
bindingAttr:
System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Instance,
binder: null,
types: new Type[] { typeof(string) },
modifiers: null);
}
private static ObjectConstructor<object> CreateParameterizedConstructor(MethodBase method)
{
if (method is null)
throw new ArgumentNullException(nameof(method));
var c = method as ConstructorInfo;
if (c != null)
return a => c.Invoke(a);
return a => method.Invoke(null, a);
}
}
}
Solution:
public Response Get(string jsonData) {
var json = JsonConvert.DeserializeObject<modelname>(jsonData);
var data = StoredProcedure.procedureName(json.Parameter, json.Parameter, json.Parameter, json.Parameter);
return data;
}
Model:
public class modelname {
public long parameter{ get; set; }
public int parameter{ get; set; }
public int parameter{ get; set; }
public string parameter{ get; set; }
}

Web API OData media type formatter when using $expand

I'm trying to create a MediaTypeFormatter to handle text/csv but running into a few problems when using $expand in the OData query.
Query:
http://localhost/RestBlog/api/Blogs/121?$expand=Comments
Controller:
[EnableQuery]
public IQueryable<Blog> GetBlog(int id)
{
return DbCtx.Blog.Where(x => x.blogID == id);
}
In my media type formatter:
private static MethodInfo _createStreamWriter =
typeof(CsvFormatter)
.GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
.Single(m => m.Name == "StreamWriter");
internal static void StreamWriter<T, X>(T results)
{
var queryableResult = results as IQueryable<X>;
if (queryableResult != null)
{
var actualResults = queryableResult.ToList<X>();
}
}
public override void WriteToStream(Type type, object value,
Stream writeStream, HttpContent content)
{
Type genericType = type.GetGenericArguments()[0];
_createStreamWriter.MakeGenericMethod(
new Type[] { value.GetType(), genericType })
.Invoke(null, new object[] { value }
);
}
Note that the type of value is System.Data.Entity.Infrastructure.DbQuery<System.Web.Http.OData.Query.Expressions.SelectExpandBinder.SelectAllAndExpand<Rest.Blog>> which means that it doesn't work.
The type of value should be IQueryable but upon casting it returns null.
When making a query without the $expand things work a lot more sensibly. What am I doing wrong?
I'm just trying to get at the data before even outputting as CSV, so guidance would be greatly appreciated.
If you look at the source code for OData Web API, you will see that
SelectExpandBinder.SelectAllAndExpand is a subclass of the generic class SelectExpandWrapper(TEntity) :
private class SelectAllAndExpand<TEntity> : SelectExpandWrapper<TEntity>
{
}
which itself is a subclass of non-generic SelectExpandWrapper:
internal class SelectExpandWrapper<TElement> : SelectExpandWrapper
{
// Implementation...
}
which in turn implements IEdmEntityObject and ISelectExpandWrapper:
internal abstract class SelectExpandWrapper : IEdmEntityObject, ISelectExpandWrapper
{
// Implementation...
}
This means that you have access to the ISelectExpandWrapper.ToDictionary method and can use it to get at the properties of the underlying entity:
public interface ISelectExpandWrapper
{
IDictionary<string, object> ToDictionary();
IDictionary<string, object> ToDictionary(Func<IEdmModel, IEdmStructuredType, IPropertyMapper> propertyMapperProvider);
}
Indeed this is how serialization to JSON is implemented in the framework as can be seen from SelectExpandWrapperConverter:
internal class SelectExpandWrapperConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
ISelectExpandWrapper selectExpandWrapper = value as ISelectExpandWrapper;
if (selectExpandWrapper != null)
{
serializer.Serialize(writer, selectExpandWrapper.ToDictionary(_mapperProvider));
}
}
// Other methods...
}
I was googled when i face that issue in my task.. i have clean implementation from this thread
first you need verify edm modelbuilder in proper way for expand
objects
you have to register edm model for blog and foreign key releations.then only it will sucess
Example
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Blog>("blog");
builder.EntitySet<Profile>("profile");//ForeignKey releations of blog
builder.EntitySet<user>("user");//ForeignKey releations of profile
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
Then you need develop this formatter ..example source code us here
applogies for my english..
First of all we need to create a class which will be derived from MediaTypeFormatter abstract class. Here is the class with its constructors:
public class CSVMediaTypeFormatter : MediaTypeFormatter {
public CSVMediaTypeFormatter() {
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}
public CSVMediaTypeFormatter(
MediaTypeMapping mediaTypeMapping) : this() {
MediaTypeMappings.Add(mediaTypeMapping);
}
public CSVMediaTypeFormatter(
IEnumerable<MediaTypeMapping> mediaTypeMappings) : this() {
foreach (var mediaTypeMapping in mediaTypeMappings) {
MediaTypeMappings.Add(mediaTypeMapping);
}
}
}
Above, no matter which constructor you use, we always add text/csv media type to be supported for this formatter. We also allow custom MediaTypeMappings to be injected.
Now, we need to override two methods: MediaTypeFormatter.CanWriteType and MediaTypeFormatter.OnWriteToStreamAsync.
First of all, here is the CanWriteType method implementation. What this method needs to do is to determine if the type of the object is supported with this formatter or not in order to write it.
protected override bool CanWriteType(Type type) {
if (type == null)
throw new ArgumentNullException("type");
return isTypeOfIEnumerable(type);
}
private bool isTypeOfIEnumerable(Type type) {
foreach (Type interfaceType in type.GetInterfaces()) {
if (interfaceType == typeof(IEnumerable))
return true;
}
return false;
}
What this does here is to check if the object has implemented the IEnumerable interface. If so, then it is cool with that and can format the object. If not, it will return false and framework will ignore this formatter for that particular request.
And finally, here is the actual implementation. We need to do some work with reflection here in order to get the property names and values out of the value parameter which is a type of object:
protected override Task OnWriteToStreamAsync(
Type type,
object value,
Stream stream,
HttpContentHeaders contentHeaders,
FormatterContext formatterContext,
TransportContext transportContext) {
writeStream(type, value, stream, contentHeaders);
var tcs = new TaskCompletionSource<int>();
tcs.SetResult(0);
return tcs.Task;
}
private void writeStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders) {
//NOTE: We have check the type inside CanWriteType method
//If request comes this far, the type is IEnumerable. We are safe.
Type itemType = type.GetGenericArguments()[0];
StringWriter _stringWriter = new StringWriter();
_stringWriter.WriteLine(
string.Join<string>(
",", itemType.GetProperties().Select(x => x.Name )
)
);
foreach (var obj in (IEnumerable<object>)value) {
var vals = obj.GetType().GetProperties().Select(
pi => new {
Value = pi.GetValue(obj, null)
}
);
string _valueLine = string.Empty;
foreach (var val in vals) {
if (val.Value != null) {
var _val = val.Value.ToString();
//Check if the value contans a comma and place it in quotes if so
if (_val.Contains(","))
_val = string.Concat("\"", _val, "\"");
//Replace any \r or \n special characters from a new line with a space
if (_val.Contains("\r"))
_val = _val.Replace("\r", " ");
if (_val.Contains("\n"))
_val = _val.Replace("\n", " ");
_valueLine = string.Concat(_valueLine, _val, ",");
} else {
_valueLine = string.Concat(string.Empty, ",");
}
}
_stringWriter.WriteLine(_valueLine.TrimEnd(','));
}
var streamWriter = new StreamWriter(stream);
streamWriter.Write(_stringWriter.ToString());
}
We are partially done. Now, we need to make use out of this. I registered this formatter into the pipeline with the following code inside Global.asax Application_Start method:
GlobalConfiguration.Configuration.Formatters.Add(
new CSVMediaTypeFormatter(
new QueryStringMapping("format", "csv", "text/csv")
)
);
On my sample application, when you navigate to /api/cars?format=csv, it will get you a CSV file but without an extension. Go ahead and add the csv extension. Then, open it with Excel and you should see something similar to below:

Deserializing a null field that previously contained a value when it was serialized

I have read many posts regarding deserialization of nullable fields but have not run across the following scenario:
Serialize an object with a nullable field that contains a value ("nil" attribute is not added to the node because it contains a value).
Remove the value from the nullable field in the xml (this happens via client-side processing).
Deserialize the xml.
Step 3 throws an error because the serializer does not treat the empty value of the nullable field as a null value (because "nil=true" is not specified). It instead tries to convert the value to the field's data type (ex: Guid), which fails resulting in an error message that varies depending on the field's data type.
In the case of a Guid the error message is:
System.InvalidOperationException: There is an error in XML document ([line number], [column number]). ---> System.FormatException: Unrecognized Guid format.
I should note that the serialization / deserialization methods we use are framework methods that use generics.
I'm looking for an elegant and generic solution. The only feasible, generic solution I can think of is the following:
Convert the xml to an XDocument.
Use (less than desired) reflection to get all of the properties of the object that are reference types.
Add "nil=true" attribute to all nodes whose name is found in the list from #2 and has an empty value.
Use recursion to process each reference type in #2.
Note: Simply adding "nil=true" to all nodes that have an empty value will not work because the serializer will throw an error for value types that cannot be null.
[Edit] Code examples:
Sample data class
public class DummyData
{
public Guid? NullableGuid { get; set; }
}
Xml sent to client
<DummyData>
<NullableGuid>052ec82c-7322-4745-9ac1-20cc4e0f142d</NullableGuid>
</DummyData>
Xml returned from client (error)
<DummyData>
<NullableGuid></NullableGuid>
</DummyData>
Xml returned from client (desired result)
<DummyData>
<NullableGuid p2:nil="true" xmlns:p2="http://www.w3.org/2001/XMLSchema-instance"></NullableGuid>
</DummyData>
Here is the solution I came up with that pretty closely resembles my plan of attack described in the original question.
Disclaimer: It is not short and most likely does not cover every deserialization scenario but seems to get the job done.
public static T FromXml<T>(string xml)
{
string convertedXml = AddNilAttributesToNullableTypesWithNullValues(typeof(T), xml);
var reader = new StringReader(convertedXml);
var serializer = new XmlSerializer(typeof (T));
var data = (T) serializer.Deserialize(reader);
reader.Close();
return data;
}
private static string AddNilAttributesToNullableTypesWithNullValues(Type type, string xml)
{
string result;
if (!string.IsNullOrWhiteSpace(xml))
{
XDocument doc = XDocument.Parse(xml);
if (doc.Root != null)
AddNilAttributesToNullableTypesWithNullValues(doc.Root, type);
result = doc.ToString();
}
else
result = xml;
return result;
}
private static void AddNilAttributesToNullableTypesWithNullValues(XElement element, Type type)
{
if (type == null)
throw new ArgumentNullException("type");
if (element == null)
throw new ArgumentNullException("element");
//If this type can be null and it does not have a value, add or update nil attribute
//with a value of true.
if (IsReferenceOrNullableType(type) && string.IsNullOrEmpty(element.Value))
{
XAttribute existingNilAttribute = element.Attributes().FirstOrDefault(a => a.Name.LocalName == NIL_ATTRIBUTE_NAME);
if (existingNilAttribute == null)
element.Add(NilAttribute);
else
existingNilAttribute.SetValue(true);
}
else
{
//Process all of the objects' properties that have a corresponding child element.
foreach (PropertyInfo property in type.GetProperties())
{
string elementName = GetElementNameByPropertyInfo(property);
foreach (XElement childElement in element.Elements().Where(e =>
e.Name.LocalName.Equals(elementName)))
{
AddNilAttributesToNullableTypesWithNullValues(childElement, property.PropertyType);
}
}
//For generic IEnumerable types that have elements that correspond to the enumerated type,
//process the each element.
if (IsGenericEnumerable(type))
{
Type enumeratedType = GetEnumeratedType(type);
if (enumeratedType != null)
{
IEnumerable<XElement> enumeratedElements = element.Elements().Where(e =>
e.Name.LocalName.Equals(enumeratedType.Name));
foreach (XElement enumerableElement in enumeratedElements)
AddNilAttributesToNullableTypesWithNullValues(enumerableElement, enumeratedType);
}
}
}
}
private static string GetElementNameByPropertyInfo(PropertyInfo property)
{
string overrideElementName = property.GetCustomAttributes(true).OfType<XmlElementAttribute>().Select(xmlElementAttribute =>
xmlElementAttribute.ElementName).FirstOrDefault();
return overrideElementName ?? property.Name;
}
private static Type GetEnumeratedType(Type type)
{
Type enumerableType = null;
Type[] types = type.GetGenericArguments();
if (types.Length == 1)
enumerableType = types[0];
return enumerableType;
}
public static bool IsGenericEnumerable(Type type)
{
return type.IsGenericType && type.GetInterfaces().Any(i =>
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
}
private static bool IsReferenceOrNullableType(Type type)
{
return !type.IsValueType || Nullable.GetUnderlyingType(type) != null;
}
private const string NIL_ATTRIBUTE_NAME = "nil";
private const string XML_SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";
private static XAttribute NilAttribute
{
get
{
if (_nilAttribute == null)
{
XNamespace xmlSchemaNamespace = XNamespace.Get(XML_SCHEMA_NAMESPACE);
_nilAttribute = new XAttribute(xmlSchemaNamespace + NIL_ATTRIBUTE_NAME, true);
}
return _nilAttribute;
}
}

How do I deserialize into an existing object - C#

In C#, after serializing an object to a file how would I deserialize the file back into an existing object without creating a new object?
All the examples I can find for custom serialization involve implementing a constructor that will be called upon deserialization which is exactly what I want except that I don't want the function to be a constructor.
Thanks!
Some serializers support callbacks; for example, both BinaryFormatter and DataContractSerializer (and protobuf-net, below) allow you to specify a before-serializaton callback, and since they skip the constructor, this may well be enough to initialize the object. The serializer is still creating it, though.
Most serializers are fussy about wanting to create the new object themselves, however some will allow you to deserialize into an existing object. Well, actually the only one that leaps to mind is protobuf-net (disclosure: I'm the author)...
This has 2 different features that might help here; for the root object (i.e. the outermost object in a graph) you can supply the existing object directly to either the Merge methods (in v1, also present in v2 for compatibility), or (in v2) the Deserialize methods; for example:
var obj = Serializer.Merge<YourType>(source, instance);
However, in a larger graph, you might want to supply other objects yourself (than just the root). The following is not exposed on the attribute API, but is a new feature in v2:
RuntimeTypeModel.Default[typeof(SomeType)].SetFactory(factoryMethod);
where factoryMethod can be either the name of a static method in SomeType (that returns a SomeType instance), or can be a MethodInfo to any static method anywhere. The method can additionally (optionally) take the serialization-context as a parameter if you want. This method should then be used to supply all new instances of SomeType.
Note: protobuf-net is not quite the same as BinaryFormatter; for best effect, you need to tell it how to map your members - very similar to marking things as [DataMember] for WCF/DataContractSerializer. This can be attributes, but does not need to be.
No problem, just use 2 classes. In getObject method you get your existing object
[Serializable]
public class McRealObjectHelper : IObjectReference, ISerializable
{
Object m_realObject;
virtual object getObject(McObjectId id)
{
return id.GetObject();
}
public McRealObjectHelper(SerializationInfo info, StreamingContext context)
{
McObjectId id = (McObjectId)info.GetValue("ID", typeof(McObjectId));
m_realObject = getObject(id);
if(m_realObject == null)
return;
Type t = m_realObject.GetType();
MemberInfo[] members = FormatterServices.GetSerializableMembers(t, context);
List<MemberInfo> deserializeMembers = new List<MemberInfo>(members.Length);
List<object> data = new List<object>(members.Length);
foreach(MemberInfo mi in members)
{
Type dataType = null;
if(mi.MemberType == MemberTypes.Field)
{
FieldInfo fi = mi as FieldInfo;
dataType = fi.FieldType;
} else if(mi.MemberType == MemberTypes.Property){
PropertyInfo pi = mi as PropertyInfo;
dataType = pi.PropertyType;
}
try
{
if(dataType != null){
data.Add(info.GetValue(mi.Name, dataType));
deserializeMembers.Add(mi);
}
}
catch (SerializationException)
{
//some fiels are missing, new version, skip this fields
}
}
FormatterServices.PopulateObjectMembers(m_realObject, deserializeMembers.ToArray(), data.ToArray());
}
public object GetRealObject( StreamingContext context )
{
return m_realObject;
}
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
}
}
public class McRealObjectBinder: SerializationBinder
{
String assemVer;
String typeVer;
public McRealObjectBinder(String asmName, String typeName)
{
assemVer = asmName;
typeVer = typeName;
}
public override Type BindToType( String assemblyName, String typeName )
{
Type typeToDeserialize = null;
if ( assemblyName.Equals( assemVer ) && typeName.Equals( typeVer ) )
{
return typeof(McRealObjectHelper);
}
typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) );
return typeToDeserialize;
}
}
Then, when deserialize:
BinaryFormatter bf = new BinaryFormatter(null, context);
bf.Binder = new McRealObjectBinder(YourType.Assembly.FullName, YourType.FullName);
bf.Deserialize(memStream);
If it's just a matter of copying a few fields, I would avoid all the trouble and take the simple route - deserialize into a new instance, then copy the appropriate fields to the existing instance. It will cost you a couple of extra copies, but you'll save a lot of time on debugging.
It's a bit unusual but it works:
[Serializable]
public class Pets
{
public int Cats { get; set; }
public int Dogs { get; set; }
}
public static class Utils
{
public static byte[] BinarySerialize(object o)
{
using (var ms = new MemoryStream())
{
IFormatter f = new BinaryFormatter();
f.Serialize(ms, o);
return ms.ToArray();
}
}
public static dynamic BinaryDeserialize(byte[] bytes, dynamic o)
{
using (var ms = new MemoryStream(bytes))
{
ms.Position = 0;
IFormatter f = new BinaryFormatter();
o = (dynamic)f.Deserialize(ms);
return o;
}
}
}
The deserialization is tricky with the dynamic which cannot be passed by reference.
And finally a no-sense test for proof:
Pets p = new Pets() { Cats = 0, Dogs = 3 };
Console.WriteLine("{0}, {1}", p.Cats, p.Dogs);
byte[] serial = Utils.BinarySerialize(p);
p.Cats = 1;
Console.WriteLine("{0}, {1}", p.Cats, p.Dogs);
p = Utils.BinaryDeserialize(serial, p);
Console.WriteLine("{0}, {1}", p.Cats, p.Dogs);
The output is the following:
0, 3
1, 3
0, 3
Replace memory strings with file stream and you will get the same results.

Categories

Resources