Check if XElement is null globally - c#

I have a class what takes care of reading and holding the XML file.
Right now a simple version of it looks like this:
public class EstEIDPersoConfig
{
public bool LaunchDebugger { get ; set; }
public string Password { get; set; }
public int Slot { get; set; }
public string Reader { get; set; }
public string TestInput { get; set; }
public bool Logging { get; set; }
public EstEIDPersoConfig()
{
XElement xml = XElement.Load(myxml.xml);
XElement Configuration = xml.Element("Configuration");
LaunchDebugger = Convert.ToBoolean(Configuration.Element("LaunchDebugger").Value);
Password = Configuration.Element("Password").Value;
Slot = Convert.ToInt32(Configuration.Element("Slot").Value);
Reader = Configuration.Element("Reader").Value;
TestInput = Configuration.Element("TestInput").Value;
Logging = Convert.ToBoolean(Configuration.Element("Logging").Value);
}
}
And there will be more later. so the problem is that if some element does not exist in xml i get System.NullReferenceException. So i need to check if the element is null or not. Heres one way to do this:
var value = Configuration.Element("LaunchDebugger").Value;
if (value != null)
LaunchDebugger = Convert.ToBoolean(value);
else
throw new Exception("LaunchDebugger element missing from xml!");
But doing that for every element would be just too much. So i need some good ideas how to simplify this system so it wouldn't end up in 1000 lines of code.
EDIT: Edited the last code snippet, the idea was not to set a default value, idea was to notify the user that this element whats null is missing from the xml.

The idea here comes directly from abatischev's answer so he deserves the credit.
As perscribed by Microsoft here you can just cast the XElement to the type you desire.
LaunchDebugger = (bool?)Configuration.Element("LaunchDebugger");
if you want to handle the null case I guess you could do
LaunchDebugger = (bool)(Configuration.Element("LaunchDebugger") ?? true);
or perhaps
LaunchDebugger = (bool)(Configuration.Element("LaunchDebugger") ?? false);
depending on your business logic. If you do the same coalescene for a specific type it may be appropriate to wrap this one liner in a method, extension or otherwise but I'm uncertain it would add much.

(bool)Configuration.Element("LaunchDebugger")
or
(bool?)Configuration.Element("LaunchDebugger")
should not throw exception.
See MSDN:
XElement Explicit Conversion (XElement to Boolean)
XElement Explicit Conversion (XElement to Nullable<Boolean>)

I have an extension method that I use for just this kind of thing:
public static T GetValue<T>(
this XElement #this,
XName name,
Func<XElement, T> cast,
Func<T> #default)
{
var e = #this.Element(name);
return (e != null) ? cast(e) : #default();
}
It gives you the casting required and also a default value factory.
Here's how you'd use it:
LaunchDebugger = Configuration.GetValue("LaunchDebugger",
x => Convert.ToBoolean(x), () => false);
Password = Configuration.GetValue("CMKPassword", x => (string)x, () => "");
Slot = Configuration.GetValue("CMKSlot", x => (int)x, () => -1);
Reader = Configuration.GetValue("Reader", x => (string)x, () => "");
TestInput = Configuration.GetValue("TestInput", x => (string)x, () => "");
Logging = Configuration.GetValue("Logging",
x => Convert.ToBoolean(x), () => false);

Extract the logic to a method and have overloaded methods for Int32,boolean and other data types conversion.
public static void GetElementValue(XElement xElement, string parameter, out bool value)
{
var stringValue = xElement.Element(parameter).Value;
value = false;
if (value != null)
value = Convert.ToBoolean(stringValue);
}

You can define a method to extract the value for you and do some checking on null there. So wrap the value retrieval in your own method like so:
public string GetXMLValue(XElement config, string elementName){
var element = Configuration.Element(elementName);
if(element == null)
return String.Empty;
return element.Value;
}
Of course you can extend this to work correctly with parsing to boolean etc.

How about an external method:
public static class XElementExtensions
{
public static bool AsBoolean(this XElement self, bool defaultValue)
{
if (self == null)
{
return defaultValue;
}
if (!string.IsNullOrEmpty(self.Value))
{
try
{
return XmlConvert.ToBoolean(self.Value);
}
catch
{
return defaultValue;
}
}
return defaultValue;
}
}
I've tested this with SnippetCompiler:
XElement test = new XElement("test",
new XElement("child1"),
new XElement("child2", new XText("true")),
new XElement("child3", new XText("false")),
new XElement("child4", new XText("rubbish")));
WL(test.Element("child1").AsBoolean(false)); // note, "child1" has no value (or is `""`)
WL(test.Element("child2").AsBoolean(false));
WL(test.Element("child3").AsBoolean(false));
WL(test.Element("child4").AsBoolean(false));
WL(test.Element("child5").AsBoolean(false)); // note, "child5" doesn't exist
To produce this result:
False
True
False
False
False
Add more such methods for other types and also add AsBoolean(defaultValue), as that can come in handy, when you want to default to true!
As others have stated, you can use the ?? operator to provide a value for null. This doesn't nest, though, so:
LaunchDebugger = XmlConvert.ToBoolean(Configuration.Element("LaunchDebugger").Value) ?? false;
will through a NullReferenceException if there is no such element in the XML file.

Related

Document Update Using Driver Ignoring _id

I'd like to be able to update a key/value pair within a Document in a Collection without regard to the _id. This SO response appears to provide a way to do what I want, but it requires a focus on the _id key.
Essentially, what can I do to get my code, as shown below, to work without adding the [BsonIgnoreExtraElements] attribute to the class?
Based on the code which I'll show below, I think I'm close, and I do have a possible workaround, using the attribute, which I'll also show. However, I'd like to do this without decorating the classes with any attributes if possible.
Why no _id? Personally, I find the _id field just gets in the way. It's not part of any of my classes, and MongoDB isn't relational, so I simply don't use it. I freely admit that I may be completely missing the intent and idea behind the use of the _id field, but I simply don't see a need for it in MongoDB. Nevertheless, if possible, I'd like to focus on working without a focus on the _id field.
With that said, I have methods for retrieving a single document or an entire collection along with inserting a single document or entire collection, generically, without regard to the _id field.
I found that the key to ignoring the _id in a "select" is to use a Projection. I'm not all that familiar with the driver, but here are the lines of code that do the magic:
public const string ELEMENT_ID = "_id";
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
For background info, here is a retrieval method, ignoring _id:
public T GetSingle<T>(string property, string value) where T : class, new()
{
T tObject = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
string className = typeof(T).ToString();
int lastPeriod = className.LastIndexOf('.');
int length = className.Length - lastPeriod;
className = className.Substring(lastPeriod + 1, length - 1);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(value);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
tObject = found.FirstOrDefault<T>();
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return tObject;
}
Similar to other another SO response, I've tried to use FindOneAndUpdate, but I receive the following error:
Element '_id' does not match any field or property of class
ClassThingy.Suffix.
If I could apply the Projection to the FindOneAndUpdate somehow, I think that might resolve my issue, but I'm not able to find a way to do that application.
Here is my code:
public T UpdateSingle<T>(T item, string property, object originalValue, object newValue) where T : class, new()
{
string className = string.Empty;
T updatedDocument = null;
try
{
if (MongoContext.MongoClient != null && MongoContext.MongoDatabase != null)
{
className = ClassUtility.GetClassNameFromObject<T>(item);
if (!string.IsNullOrEmpty(className))
{
IMongoCollection<T> mongoCollection = MongoContext.MongoDatabase.GetCollection<T>(className);
if (mongoCollection != null)
{
BsonDocument bsonDocument = new BsonDocument();
ProjectionDefinition<T> projection = Builders<T>.Projection.Exclude(ELEMENT_ID);
PropertyInfo[] propertyInfo = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
if (propertyInfo != null && propertyInfo.Length > 0)
{
IEnumerable<PropertyInfo> piExisting = propertyInfo.Where(pi => pi.Name.Equals(property, StringComparison.CurrentCultureIgnoreCase));
if (piExisting != null && piExisting.Any())
{
BsonValue bsonValue = BsonValue.Create(originalValue);
BsonElement bsonElement = new BsonElement(property, bsonValue);
if (bsonElement != null)
{
bsonDocument.Add(bsonElement);
}
IFindFluent<T, T> found = mongoCollection.Find(bsonDocument).Project<T>(projection);
if (found != null)
{
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition);
}
}
}
}
}
}
}
catch (Exception ex)
{
Logger.WriteToLog(Logger.LoggerMessage(ex));
}
return updatedDocument;
}
Interestingly enough, while the FindOneAndUpdate method actually appears to succeed and the "Suffix" collection does, in fact, get modified. Also, the return doesn't contain the modification. Instead, it's the "original" (when I use the workaround as shown below).
More Info:
Suffix Class:
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
Suffix suffix = new Suffix();
MongoRepository.MongoRepository mongoRepository = new MongoRepository.MongoRepository("MyDataBase");
mongoRepository.UpdateSingle<Suffix>(suffix, "Description", "Jr", "Junior");
Workaround:
[BsonIgnoreExtraElements]
public class Suffix
{
public string Code { get; set; }
public string Description { get; set; }
}
But, much rather not use the attribute if at all possible.
One thing you're missing here is the third parameter of .FindOneAndUpdate() method which is of type FindOneAndUpdateOptions<T,T>. That's the place where you can define if you want to get document After or Before the modification. Before is the default value Moreover you can specify the projection and exclude _id property. Try:
FilterDefinition<T> filterDefinition = Builders<T>.Filter.Eq(property, originalValue);
UpdateDefinition<T> updateDefinition = Builders<T>.Update.Set(property, newValue);
ProjectionDefinition<T, T> projection = Builders<T>.Projection.Exclude("_id");
var options = new FindOneAndUpdateOptions<T>()
{
Projection = projection,
ReturnDocument = ReturnDocument.After
};
updatedDocument = mongoCollection.FindOneAndUpdate<T>(filterDefinition, updateDefinition, options);

How can I make a field in a class be set to false if the value being set is a null?

I have the following:
var result2 = result1
.Select((t, index) => new {
Answer = t.Answer,
Answers = JSON.FromJSONString<Answer2>(t.AnswerJSON)
});
return Ok(result2);
public class Answer2 {
public bool? Correct; // Maybe this should be a property
public bool Response; // Maybe this should be a property
}
And my String > object function:
public static T FromJSONString<T>(this string obj) where T : class
{
if (obj == null)
{
return null;
}
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(obj)))
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
T ret = (T)ser.ReadObject(stream);
return ret;
}
}
Is there a way with this that I could make the Response field be false if a null is present for Response in the JSON string or if there is no value for Response in the JSON string?
Note: I had a suggestion about using a property and I think that would work but I am not sure how to do it in practice.
You should use a property for this matter:
public class Answer2 {
private bool correct; // This field has no need to be nullable
public bool? Correct
{
get { return correct; }
set { correct = value.GetValueOrDefault(); }
}
}
Following the Q and A section, you should be able to do it on a property like this:
private bool? whatever;
public bool? Whatever
{
get { return whatever; }
set
{
if (value == null)
whatever = false;
else
whatever = value;
}
}
This way you can pass a null value to the property, but it can only have a bool (true/false) value in it.

Is there such a thing as a chained NULL check?

I have the following ugly code:
if (msg == null ||
msg.Content == null ||
msg.Content.AccountMarketMessage == null ||
msg.Content.AccountMarketMessage.Account == null ||
msg.Content.AccountMarketMessage.Account.sObject == null) return;
Is there a way to chain check for null values in C#, so that I don't have to check each individual level?
One of the proposals in C# 6 would be to add a new Null Propogation operator.
This will (hopefully) allow you to write:
var obj = msg?.Content?.AccountMarketMessage?.Account?.sObject;
if (obj == null) return;
Unfortunately, there is nothing in the language at this point that handles this.
There is not currently such a thing, but it may be coming to .NET very soon. There is a well-known User Voice thread on the subject. And as noted in this article, the Visual Studio team has recently announced that:
We are seriously considering this feature for C# and VB, and will be
prototyping it in coming months.
Edit: and as noted in Reed Copsey's answer above, it is now a planned addition for C# 6. There are better details on the Codeplex pages he linked.
There is no built-in support for this, but you can use an extension method for that:
public static bool IsNull<T>(this T source, string path)
{
var props = path.Split('.');
var type = source.GetType();
var currentObject = type.GetProperty(props[0]).GetValue(source);
if (currentObject == null) return true;
foreach (var prop in props.Skip(1))
{
currentObject = currentObject.GetType()
.GetProperty(prop)
.GetValue(currentObject);
if (currentObject == null) return true;
}
return false;
}
Then call it:
if ( !msg.IsNull("Content.AccountMarketMessage.Account.sObject") ) return;
You need monads and Monadic null checking. Could have a look at Monads.Net package. It can help with simplifying null tests and getting values from deep navigation properties
Something like
var sObject = person.With(p=>p.Content).With(w=>w.AccountMarketMessage ).With(p=>p.Account).With(p=>p.Object);
If you wanted a default value then
var sObject = person.With(p=>p.Content).With(w=>w.AccountMarketMessage).With(p=>p.Account).Return(p=>p.Object, "default value");
You can lazily evaluate the values using lambda expressions. This is overkill for a simple null check, but can be useful for chaining more complex expressions in a "fluent" manner.
Example
// a type that has many descendents
var nested = new Nested();
// setup an evaluation chain
var isNull =
NullCheck.Check( () => nested )
.ThenCheck( () => nested.Child )
.ThenCheck( () => nested.Child.Child )
.ThenCheck( () => nested.Child.Child.Child )
.ThenCheck( () => nested.Child.Child.Child.Child );
// handle the results
Console.WriteLine( isNull.IsNull ? "null" : "not null" );
Code
This is a full example (albeit draft-quality code) that can be pasted into a console app or LINQPad.
public class Nested
{
public Nested Child
{
get;
set;
}
}
public class NullCheck
{
public bool IsNull { get; private set; }
// continues the chain
public NullCheck ThenCheck( Func<object> test )
{
if( !IsNull )
{
// only evaluate if the last state was "not null"
this.IsNull = test() == null;
}
return this;
}
// starts the chain (convenience method to avoid explicit instantiation)
public static NullCheck Check( Func<object> test )
{
return new NullCheck { IsNull = test() == null };
}
}
private void Main()
{
// test 1
var nested = new Nested();
var isNull =
NullCheck.Check( () => nested )
.ThenCheck( () => nested.Child )
.ThenCheck( () => nested.Child.Child )
.ThenCheck( () => nested.Child.Child.Child )
.ThenCheck( () => nested.Child.Child.Child.Child );
Console.WriteLine( isNull.IsNull ? "null" : "not null" );
// test 2
nested = new Nested { Child = new Nested() };
isNull = NullCheck.Check( () => nested ).ThenCheck( () => nested.Child );
Console.WriteLine( isNull.IsNull ? "null" : "not null" );
// test 3
nested = new Nested { Child = new Nested() };
isNull = NullCheck.Check( () => nested ).ThenCheck( () => nested.Child ).ThenCheck( () => nested.Child.Child );
Console.WriteLine( isNull.IsNull ? "null" : "not null" );
}
Again: you probably shouldn't use this in lieu of simple null checks due to the complexity it introduces, but it's an interesting pattern.
.NET Fiddle
As stated there is a plan to have c# 6.0 implement the ? operator to facilitate this process somewhat. If you cannot wait, I would suggest using a lambda expression and a simple helper function to solve this.
public E NestedProperty<T,E>(T Parent, Func<T,E> Path, E IfNullOrEmpty = default(E))
{
try
{
return Path(Parent);
}
catch
{
return IfNullOrEmpty;
}
}
This could be used int value = NestedProperty<First,int>(blank,f => f.Second.Third.id); as shown in the demo below
program
public class Program
{
public void Main()
{
First blank = new First();
First populated = new First(true);
//where a value exists
int value = NestedProperty<First,int>(blank,f => f.Second.Third.id);
Console.WriteLine(value);//0
//where no value exists
value = NestedProperty<First,int>(populated,f => f.Second.Third.id);
Console.WriteLine(value);//1
//where no value exists and a default was used
value = NestedProperty<First,int>(blank,f => f.Second.Third.id,-1);
Console.WriteLine(value);//-1
}
public E NestedProperty<T,E>(T Parent, Func<T,E> Path, E IfNullOrEmpty = default(E))
{
try
{
return Path(Parent);
}
catch
{
return IfNullOrEmpty;
}
}
}
simple demo structure
public class First
{
public Second Second { get; set; }
public int id { get; set; }
public First(){}
public First(bool init)
{
this.id = 1;
this.Second = new Second();
}
}
public class Second
{
public Third Third { get; set; }
public int id { get; set; }
public Second()
{
this.id = 1;
this.Third = new Third();
}
}
public class Third
{
public int id { get; set; }
public Third()
{
this.id = 1;
}
}
Since 3.5 (maybe earlier), You could write very simple extension method
public static TResult DefaultOrValue<T, TResult> (this T source,
Func<T, TResult> property) where T : class
{
return source == null ? default(TResult) : property(source);
}
You may name this method even shortier and then use like this
var instance = new First {SecondInstance = new Second
{ThirdInstance = new Third {Value = 5}}};
var val =
instance .DefaultOrValue(x => x.SecondInstance)
.DefaultOrValue(x => x.ThirdInstance)
.DefaultOrValue(x => x.Value);
Console.WriteLine(val);
Console.ReadLine();
so source classes are:
public class Third
{
public int Value;
}
public class First
{
public Second SecondInstance;
}
public class Second
{
public Third ThirdInstance;
}

How to check all properties of an object whether null or empty?

I have an object lets call it ObjectA
and that object has 10 properties and those are all strings.
var myObject = new {Property1="",Property2="",Property3="",Property4="",...}
is there anyway to check to see whether all these properties are null or empty?
So any built-in method that would return true or false?
If any single of them is not null or empty then the return would be false. If all of them are empty it should return true.
The idea is I do not want to write 10 if statement to control if those properties are empty or null.
Thanks
You can do it using Reflection
bool IsAnyNullOrEmpty(object myObject)
{
foreach(PropertyInfo pi in myObject.GetType().GetProperties())
{
if(pi.PropertyType == typeof(string))
{
string value = (string)pi.GetValue(myObject);
if(string.IsNullOrEmpty(value))
{
return true;
}
}
}
return false;
}
Matthew Watson suggested an alternative using LINQ:
return myObject.GetType().GetProperties()
.Where(pi => pi.PropertyType == typeof(string))
.Select(pi => (string)pi.GetValue(myObject))
.Any(value => string.IsNullOrEmpty(value));
I suppose you want to make sure that all properties are filled in.
A better option is probably by putting this validation in the constructor of your class and throw exceptions if validation fails. That way you cannot create a class that is invalid; catch exceptions and handle them accordingly.
Fluent validation is a nice framework (http://fluentvalidation.codeplex.com) for doing the validation. Example:
public class CustomerValidator: AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.Property1).NotNull();
RuleFor(customer => customer.Property2).NotNull();
RuleFor(customer => customer.Property3).NotNull();
}
}
public class Customer
{
public Customer(string property1, string property2, string property3)
{
Property1 = property1;
Property2 = property2;
Property3 = property3;
new CustomerValidator().ValidateAndThrow();
}
public string Property1 {get; set;}
public string Property2 {get; set;}
public string Property3 {get; set;}
}
Usage:
try
{
var customer = new Customer("string1", "string", null);
// logic here
} catch (ValidationException ex)
{
// A validation error occured
}
PS - Using reflection for this kind of thing just makes your code harder to read. Using validation as shown above makes it explicitly clear what your rules are; and you can easily extend them with other rules.
The following code returns if any property is not null.
return myObject.GetType()
.GetProperties() //get all properties on object
.Select(pi => pi.GetValue(myObject)) //get value for the property
.Any(value => value != null); // Check if one of the values is not null, if so it returns true.
Here you go
var instOfA = new ObjectA();
bool isAnyPropEmpty = instOfA.GetType().GetProperties()
.Where(p => p.GetValue(instOfA) is string) // selecting only string props
.Any(p => string.IsNullOrWhiteSpace((p.GetValue(instOfA) as string)));
and here's the class
class ObjectA
{
public string A { get; set; }
public string B { get; set; }
}
A slightly different way of expressing the linq to see if all string properties of an object are non null and non empty:
public static bool AllStringPropertyValuesAreNonEmpty(object myObject)
{
var allStringPropertyValues =
from property in myObject.GetType().GetProperties()
where property.PropertyType == typeof(string) && property.CanRead
select (string) property.GetValue(myObject);
return allStringPropertyValues.All(value => !string.IsNullOrEmpty(value));
}
Note if you've got a data structural hierarchy and you want to test everything in that hierarchy, then you can use a recursive method. Here's a quick example:
static bool AnyNullOrEmpty(object obj) {
return obj == null
|| obj.ToString() == ""
|| obj.GetType().GetProperties().Any(prop => AnyNullOrEmpty(prop.GetValue(obj)));
}
To only check if all properties are null:
bool allPropertiesNull = !myObject.GetType().GetProperties().Any(prop => prop == null);
you can use reflection and extension methods to do this.
using System.Reflection;
public static class ExtensionMethods
{
public static bool StringPropertiesEmpty(this object value)
{
foreach (PropertyInfo objProp in value.GetType().GetProperties())
{
if (objProp.CanRead)
{
object val = objProp.GetValue(value, null);
if (val.GetType() == typeof(string))
{
if (val == "" || val == null)
{
return true;
}
}
}
}
return false;
}
}
then use it on any object with string properties
test obj = new test();
if (obj.StringPropertiesEmpty() == true)
{
// some of these string properties are empty or null
}
No, I don't think there is a method to do exactly that.
You'd be best writing a simple method that takes your object and returns true or false.
Alternatively, if the properties are all the same, and you just want to parse through them and find a single null or empty, perhaps some sort of collection of strings would work for you?
You can try the following query :
if the object is "referenceKey" (where few properties may be null )
referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) == null)
I need to count the properties where the value is set to not Null, so I have used the following query :
var countProvidedReferenceKeys = referenceKey.GetType().GetProperties().Where(x => x.GetValue(referenceKey) != null).Count();

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;
}
}

Categories

Resources