I want to deserialize an xml document to a class, which is genereated by the concerning xsd files. I don't have control over the contents of the xml-file.
During deserialization I run into an exception, because an enum value in the xml document does not meet the requiremnts of the xsd. Instead of breaking, i would like the deserialization to continue and just take the default value for any such errors. Is there any way to accomplish this behaviour?
edit:
For clarification, what i am trying to achieve: I want to read data from digital invoices. So the creation of the xml file is some kind of blackbox and can contain possibly flase values, even if the structure meets the standards. But that does not mean, that every value is flawed in that way. The exception prevents me from reading the correct values so i just want the deserialization to finish by somehow inserting the default values if such an error occurs.
Neither marking the values as obsolete, nor flagging them with XmlIgnore won't work, because the next xml i receive could contain correct values.
I hope that helped clarifying the problem.
Right now, im using the System.Xml.Serialization dll, but im willing to implement any library which can help me achieve the wanted behaviour.
The exception im getting:
"System.InvalidOperationException: Instance validation error: 'x' is
not a valid value for xType.."
The code that throws the exception:
XmlSerializer serializer = new xml.XmlSerializer(typeof(MyType));
MyType invoice = serializer.Deserialize(memoryStream) as MyType;
I know the code does not help very much, so I'll add the enum, that is currently problematic:
public enum PaymentMeansCodeContentType
{
[System.Xml.Serialization.XmlEnumAttribute("10")]
Item10,
[System.Xml.Serialization.XmlEnumAttribute("20")]
Item20,
[System.Xml.Serialization.XmlEnumAttribute("30")]
Item30,
[System.Xml.Serialization.XmlEnumAttribute("48")]
Item48,
[System.Xml.Serialization.XmlEnumAttribute("49")]
Item49,
[System.Xml.Serialization.XmlEnumAttribute("57")]
Item57,
[System.Xml.Serialization.XmlEnumAttribute("58")]
Item58,
[System.Xml.Serialization.XmlEnumAttribute("59")]
Item59,
ZZZ,
}
These are autogenerated from using the xsd command line tool:
https://learn.microsoft.com/de-de/dotnet/standard/serialization/xml-schema-definition-tool-xsd-exe
The xml i need to deserialize provides me with a '1', so clearly an invalid value. Still i need to access the other valid values from the xml and provide means for indicating which values are flawed.
You can mark the member Obsolete
public enum TypeEnum
{
Temperature,
Pressure,
[Obsolete]
Humidity
}
More info - docs
I still wasn't able to find the simple answer I was hoping for but managed to find a work around that worked for me. I ended up validating every enum in the XML file beforehand against the possible values. If the XML did not match the enum i saved the wrong value and node to a validation result set and overwrote the xml with the enum default value.
As Martin mentioned, it's a bit difficult to answer without proper context or sample code. However, you may want to look at the XmlIgnoreAttribute decorator on the property for the model. See the URL & code sample below for more details on how to use:
https://learn.microsoft.com/en-us/dotnet/api/system.xml.serialization.xmlattributes.xmlignore?view=netframework-4.8
using System;
using System.IO;
using System.Xml.Serialization;
// This is the class that will be serialized.
public class Group
{
// The GroupName value will be serialized--unless it's overridden.
public string GroupName;
/* This field will be ignored when serialized--
unless it's overridden. */
[XmlIgnoreAttribute]
public string Comment;
}
You can use Extended Xml Serializer library (avaliable via nuget) instead of the default XmlSerializer, with a custom converter that will set the default value in case of an error.
I suggest you store the value of the Enum as a string, and then parse it yourself. This is relatively simple to implement, here's an example:
public enum MyEnum
{
Default, //The default value to apply in the event of an invalid Enum value
[XmlEnumAttribute("10")]
Item10,
[XmlEnumAttribute("20")]
Item20
}
public class MyClass
{
public string Value { get; set; }
public MyEnum EnumValue => (MyEnum)(typeof(MyEnum).GetFields().FirstOrDefault(f =>
f.GetCustomAttribute<XmlEnumAttribute>()?.Name == Value)?
.GetValue(null) ?? MyEnum.Default);
}
Or if you prefer it you can also set a nullable Enum
public enum MyEnum
{
[XmlEnumAttribute("10")]
Item10,
[XmlEnumAttribute("20")]
Item20
}
public class MyClass
{
public string Value { get; set; }
public MyEnum? EnumValue => (MyEnum?)typeof(MyEnum).GetFields().FirstOrDefault(f =>
f.GetCustomAttribute<XmlEnumAttribute>()?.Name == Value)?
.GetValue(null);
}
Related
The title asks it all. In the database I have legacy data that contains titles of documents that have spaces such as "Title Holder" and so on. I want to be able to map these directly to an enum with Fluent NHibernate but I am encountering parsing errors. I have been unable to find any indication of a custom converter I can use, are there any recommendations someone can make?
My mapping looks like this.
Map(x => x.DocumentName).Nullable().CustomSqlType("varchar(50)");
You are going to need to implement your own NHibernate IUserType and the bulk of your logic will be in the NullSafeGet() and NullSafeSet() methods.
You'll also need to create your own internal enum to string mapping. You could use a dictionary that would hold the string as the key and the enum value as the value and so your logic would basically revolve around looking up values in that dictionary to convert from a string to an enum and vice versa. Another option could be to use an attribute to decorate each of your enum values with the string version of it's name and then at runtime do conversion with reflection...
Here are some examples of creating a custom IUserType: ( The first link below should really point you in the right direction )
Mapping Strings to Booleans Using NHibernate’s IUserType
Mapping different types - IUserType
Implementing custom types in nHibernate
It is possible to write a custom type that gets rid of the spaces when the data is read from database and then you can map the converted string to an enum. Problem with this approach would be when saving data back to database because you would not know where to add the space back in (unless you are happy with spaghetti code to keep track of where to insert the spaces back).
Alternatively, you can have an additional property on the class of type enum that returns the enum based on what is in the property mapped to database. Example below
public class Document
{
public virtual string DocumentName {get; set;}
public EDocumentName Name
{
get
{
if (DocumentName == "Title Holder")
{
return EDocumentName.TitleHolder;
}
}
set
{
if(value == EDocumentName.TitleHolder)
{
DocumentName = "Title Holder";
}
}
}
}
public enum EDocumentName
{
TitleHoldder
}
I'm currently attempting to build a service to retrieve and serialize a Sitecore data item to JSON, so our Javascript code can access Sitecore content data.
I've tried serializing the object directly with JavascriptSerializer and JSON.Net; both broke due to recursion likely due to the various circular references on the child properties.
I've also attempted to serialize the item to XML (via item.GetOuterXml()), then converting the Xml to JSON. The conversion worked fine; but it only retrieves fields that were set on the item itself, not the fields that were set in the _standardvalues. I tried calling item.Fields.ReadAll() before serializing, as well as a foreach loop with calls to item.Fields.EnsureField(Field.id); however, neither resulted in retrieving the missing fields. However, debugging the code; the Fields array appears to contain all inherited fields from its base template as well as the ones set on the item; so I'm guessing GetOuterXml is just ignoring all fields that weren't set specifically on the item.
The more I look at this, the more it looks like I'm going to need a custom model class to encapsulate the data item and the necessary fields, decorate it with the appropriate JSON.Net serialization attributes, and serialize from there. This feels like a dirty hack though.
So before I go down this road; I wanted to know if anyone here had experience serializing Sitecore content items to JSON for client-side consumption, and is there an easier way that I'm missing. Any constructive input is greatly appreciated.
Cheers,
Frank
I would suggest pursuing your approach of creating a custom model class to encapsulate just the item data you need to pass to the client. Then serialize that class to JSON. This cuts down on the amount of data you're sending over the wire and allows you to be selective about which data are being sent (for security reasons).
The CustomItem pattern and partial classes lend themselves to this approach very well. In the code samples below, the .base class is your base custom item wrapper. You can use this class to access fields and field values in a strongly-typed manner. The .instance class could be used for JSON serialization.
By splitting out the properties you want serialized, you have granular control over the data being sent back to the requesting client and you don't have to worry as much about circular references. If you need to make any changes to field definitions, you could simply change your .base class with minimal impact on your JSON serialization.
Hope this helps!
MyCustomItem.base.cs
public partial class MyCustomItem : Sitecore.Data.Items.CustomItem
{
public const string TitleFieldName = "Title";
public MyCustomItem(Item innerItem) : base(innerItem)
{
}
public static implicit operator MyCustomItem(Item innerItem)
{
return innerItem != null ? new MyCustomItem(innerItem) : null;
}
public static implicit operator Item(MyCustomItem customItem)
{
return customItem != null ? customItem.InnerItem : null;
}
public string Title
{
get { return InnerItem[TitleFieldName]); }
}
}
MyCustomItem.instance.cs
[JsonObject(MemberSerialization.OptIn)]
public partial class MyCustomItem
{
[JsonProperty("Title")]
public string JsonTitle
{
get { return Title; }
}
}
I wonder if you wouldn't be better off using an XSLT to recursively build the JSON?
I have a list of strings which are candidates for Enumerations values. They are
Don't send diffs
500 lines
1000 lines
5000 lines
Send entire diff
The problem is that spaces, special characters are not a part of identifiers and even cannot start with a number, so I would be sanitizing these values to only chars, numbers and _
To keep the original values I thought of putting these strings in the DescriptionAttribute, such that the final Enum should look like
public enum DiffBehvaiour
{
[Description("Don't send diffs")]
Dont_send_diffs,
[Description("500 lines")]
Diff_500_lines,
[Description("1000 lines")]
Diff_1000_lines,
[Description("5000 lines")]
Diff_5000_lines,
[Description("Send entire diff")]
Send_entire_diff
}
Then later using code I will retrieve the real string associated with the enumeration value, so that the correct string can be sent back the web service to get the correct resource.
I want to know how to create the DescriptionAttribute using System.Reflection.Emit
Basically the question is where and how to store the original string so that when the Enumeration value is chosen, the corresponding value can be retrieved.
I am also interested in knowing how to access DescriptionAttribute when needed.
Ok, if you really want to use reflection:
DiffBehvaiour value = DiffBehvaiour.Dont_send_diffs;
FieldInfo enumField = value.GetType().GetField(value.ToString());
DescriptionAttribute attribute = (DescriptionAttribute)enumField.
GetCustomAttributes(typeof(DescriptionAttribute), true)[0];
Console.WriteLine(attribute.Description);
$> Don't send diffs
Obviously there is no error handling, etc, but the basic idea is there.
Update
I now think I see the point of your question, which myself and the other people that answered actually missed.
You want to decorate an enum with attributes at runtime i.e. add attributes to a type at runtime. Adding attributes to a type at runtime is not possible.
However these is support in the .Net for a type metadata engine via : TypeDescritor:
MSDN http://msdn.microsoft.com/en-us/library/system.componentmodel.typedescriptor.aspx
Example http://geekswithblogs.net/abhijeetp/archive/2009/01/10/dynamic-attributes-in-c.aspx
The TypeDescritor framework allows you to dynamically provide type information rather than actually dynamically decorating types directly - it is a layer of indirection.
You may be able to bend this mechanism to support what you want to do, but at the end of the day you will need to maintain a lookup for your enum members to provide the description strings. Using a lookup structure to maintain a mapping between your enum members and description string was my first answer and the first answer to this question...
You could write a generic method like this:
class EnumExtensions
{
public static string GetDescription<TEnum>(TEnum value)
// inexpressible generic constraint TEnum : System.Enum
{
// reflection lookup of this value per #chibacity answer
}
public static IDictionary<TEnum,string> GetDescriptions<TEnum>()
// inexpressible generic constraint TEnum : System.Enum
{
// do the reflection lookups once and build a dictionary
var result = new Dictionary<TEnum, string>();
foreach(string name in Enum.GetNames(typeof(TEnum))
{
var value = (TEnum)Enum.Parse(typeof(TEnum), name);
var description = GetDescription(value);
result.Add(value, description);
}
return result;
}
}
Basically, the initial problem is I need to make a boolean value serialize as 0 or 1. The solution I found was to implement IXmlSerializable, which I did. Unfortunately the class I'm trying to serialize is generated code off a schema and has an XmlTypeAttribute on it. When I try to (de)serialize the object with the XmlSerializer created in the usual manner ( new XmlSerializer(type)) it throws this exception:
System.InvalidOperationException: Only XmlRoot attribute may be specified for the type ______ Please use XmlSchemaProviderAttribute to specify schema type.
Two options come to mind immediatly:
1) remove the attribute in the generated code.
This change would have to be made every time the code was re-generated.
2) Use an XmlAttributeOverrides object when creating the serializer to remove the attribute. This would require the rest of the code base to "know" that it needs to override that attribute. Also, the exception thrown gives absolutly no clue as to what needs to be done to fix it.
Both options kinda stink. Is there a third option?
I have the same problem, for me removing the IXMLSerializable works, I don't use it, and have you tried to hide the true or false with a some logic in the properties? Like this:
private bool mblnFlag;
public String Flag
{
get
{
return mblnFlag;
}
set
{
mblnFlag = (value == "1")
}
}
Of course you should enhance the properties and do more checking, but that's the idea.
I have a a property defined as:
[XmlArray("delete", IsNullable = true)]
[XmlArrayItem("contact", typeof(ContactEvent)),
XmlArrayItem("sms", typeof(SmsEvent))]
public List<Event> Delete { get; set; }
If the List<> Delete has no items
<delete />
is emitted. If the List<> Delete is set to null
<delete xsi:nil="true" />
is emitted. Is there a way using attributes to get the delete element not to be emitted if the collection has no items?
Greg - Perfect thanks, I didn't even read the IsNullable documentation just assumed it was signalling it as not required.
Rob Cooper - I was trying to avoid ISerializable, but Gregs suggestion works. I did run into the problem you outlined in (1), I broke a bunch of code by just returning null if the collection was zero length. To get around this I created a EventsBuilder class (the class I am serializing is called Events) that managed all the lifetime/creation of the underlying objects of the Events class that spits our Events classes for serialization.
I've had the same issue where I did not want an element outputted if the field is empty or 0.
The XML outputted could not use xsi:null="true" (by design).
I've read somewhere that if you include a property of type bool with the same name as the field you want to control but appended with 'Specified', the XMLSerializer will check the return value of this property to determine if the corresponding field should be included.
To achieve this without implementing IXMLSerializer:
public List<Event> Delete { get; set; }
[XMLIgnore]
public bool DeleteSpecified
{
get
{
bool isRendered = false;
if (Delete != null)
{
isRendered = (Delete.Count > 0);
}
return isRendered;
}
set
{
}
}
If you set IsNullable=false or just remove it (it is false by default), then the "delete" element will not be emitted. This will work only if the collection equals to null.
My guess is that there is a confusion between "nullability" in terms of .NET, and the one related to nullable elements in XML -- those that are marked by xml:nil attribute. XmlArrayAttribute.IsNullable property controls the latter.
First off, I would say ask yourself "What is Serialization?".
The XmlSerializer is doing exactly what it is supposed to be doing, persisting the current state of the object to XML. Now, I am not sure why the current behaviour is not "right" for you, since if you have initialized the List, then it is initialized.
I think you have three options here:
Add code to the Getter to return null if the collection has 0 items. This may mess up other code you have though.
Implement the IXmlSerializable interface and do all the work yourself.
If this is a common process, then you may want to look at my question "XML Serialization and Inherited Types" - Yes, I know it deals with another issue, but it shows you how to create a generic intermediary serialization class that can then be "bolted on" to allow a serilization process to be encapsulated. You could create a similar class to deal with overriding the default process for null/zero-item collections.
I hope this helps.
You could always implement IXmlSerializer and perform the serialization manually.
See http://www.codeproject.com/KB/cs/IXmlSerializable.aspx for an example.