Skip to content

ChrisMissal/Bender

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Bender Build Status

Bender is a simple xml and json de/serialization library for .NET. Unlike the JavaScriptSerializer, XmlSerializer and DataContractSerializer, Bender gives you complete control over how values are de/serialized. Bender is ~%10 faster than the XmlSerializer and JavaScriptSerializer.

Install

Bender can be found on nuget:

PM> Install-Package Bender

Usage

There are a number of ways to use Bender. The simplest is to use the convenience methods which can be optionally configured, for example:

var model = Deserialize.Json<Model>("{ ... }");
var model = Deserialize.Xml(typeof(Model), "<Model>...</Model>");

var json = Serialize.Json(model);
var xml = Serialize.Xml(model, x => x.PrettyPrintXml());

The serializer and deserializer can also be created with static factory methods that can be optionally configured, for example:

var serializer = Serializer.Create();
var serializer = Serializer.Create(x => x.PrettyPrintXml().ExcludeNullValues());

var deserializer = Deserializer.Create();
var deserializer = Deserializer.Create(x => x.ExcludeType<Token>().ExcludeType<Password>());

Finally a serializer and deserializer can also be instantiated directly and an options object can be passed in:

var serializer = new Serializer(new Options {...});

var deserializer = new Deserializer(new Options {...});

Overriding de/serialization

To override de/serialization add a reader or writer:

var serializer = Serializer.Create(x => x
    .AddWriter<byte[]>(x => { if (x.Value != null) x.Node.Value = Convert.ToBase64String(x.Value); });

var deserializer = Deserializer.Create(x => x
    .AddReader<byte[]>(x => Convert.FromBase64String(x.Node.Value));

Both readers and writers are passed a context that contains the current Options, PropertyInfo, source/target value and ValueNode. Here you can fully control the reading and rendering of the node. The Value property gives you access to the source or target property value. The Node property gives you generic Name and Value access to xml attributes, xml elements and json fields. You can use the NodeType property to determine the exact type of node and then access it directly via the XmlAttribute, XmlElement and JsonField properties if you need work with node specific properties. In most cases though you will just return or set the Value of the node, as demonstrated above.

Note: the byte[] reader/writer shown above is automatically added by default so you get that behavior out of the box.

Bender allows you to override nullable and non-nullable type de/serialization separately if you want to have fine grained control, for example:

var serializer = Serializer.Create(x => x
    .AddWriter<bool>(x => x.Node.Value = x.Value.ToString().ToLower())
    .AddWriter<bool?>(x => x.Node.Value = x.Value.HasValue ? x.Value.ToString().ToLower() ? ""));

But most of the time the functionality will be the same for nullable and non nullable readers and writers, save the boilerplate null checking logic. So Bender also allows you to set one reader or writer for both nullable and non-nullable types by passing true to the handleNullable parameter:

var serializer = Serializer.Create(x => x
    .AddWriter<bool>(x => x.Node.Value = x.Value.ToString().ToLower(), true);

Note: the bool writer shown above is automatically added by default so you get that behavior out of the box.

Writers can also be used to further process the xml that is produced. Two overloads of AddWriter, without the generic type specification, allow you to operate on all elements and attributes. These writers are run after the value writers mentioned above and more than one can be specified. The following example demonstrates how to add a null attribute to elements that have a null value:

var serializer = Bender.Serializer.Create(x => x
    .AddXmlNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")
    .AddWriter(x => x.Node.NodeType == ValueNodeType.Element && x.Value == null,
               x => x.Node.XmlElement.Add(new XAttribute(x.Options.Namespaces["xsi"] + "nil", "true"))));

Deserialization errors

Errors during deserialization can result from either the source xml or from issues with your code and configuration. In a web service or application the former can likely be addressed by your end users. With that in mind, all Bender deserialization exceptions that are a result of the source xml or json inherit from SourceException. This exception has a property called FriendlyMessage which can be displayed to end users and give them information to help them resolve the problem. There are three exceptions that inherit from SourceException: SourceParseException, UnmatchedNodeException and ValueParseException. An SourceParseException occurs when the xml or json is malformed. An UnmatchedNodeException occurs when an element or attribute does not match a property in the target type (This behavior can be configured in the deserialization options). And finally a ValueParseException occurs when a simple type cannot be parsed because it is not formatted properly. Bender has default friendly messages for simple types and these can be overriden by calling the WithFriendlyParseErrorMessage<T>(string message) method in the deserialization options.

When creating your own custom readers you can make use of friendly error messages by specifying one for the type your reader handles:

var deserializer = Deserializer.Create(x => x
  .WithFriendlyParseErrorMessage<IPAddress>("Not formatted correctly, must be formatted as '1.2.3.4'.")
  .AddReader<IPAddress>(x => IPAddress.Parse(n.Value));

Any errors resulting from the reader will be wrapped in a ValueParseException with the friendly error specified.

Miscellania

  • Bender supports the XmlRootAttribute, XmlTypeAttribute, XmlElementAttribute, XmlAttributeAttribute, XmlArrayAttribute and XmlArrayItemAttribute to override element naming as the XmlSerializer does.
  • Bender supports the XmlIgnoreAttribute to ignore properties as the XmlSerializer does.
  • Bender will de/serialize nullable types and enumerations.
  • Bender will pass the parent object to into the constructor of the child object on deserialization if a constructor is defined with a single argument of the parent type.
  • Bender de/serializes the following basic types out of the box: String, Char, Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Guid, TimeSpan, IEnumerable<T>, List<T>, IList<T>, Array, ArrayList (Serialization only), IEnumerable (Serialization only), byte[] (As base64), MailAddress, Version, IPAddress and Uri.
  • Bender will automatically deserialize values in either attributes or elements. By default values are serialized as elements but this can be changed to attributes in configuration.

Bender also includes a helper for building out object graphs. This can be usefull for creating larger object graphs for tests:

var model = Expander.Expand<Model>();

Configuration

The following are the common configuration options:

ExcludeTypes(Func<Type, bool> typeFilter) Allows you to exclude types based on the predicate passed in. This method is additive so it can be called multiple times.
ExcludeType<T>() Exclude a particular type.
WithDefaultGenericTypeXmlNameFormat(string typeNameFormat) This is the format of generic xml type element names that haven't been decorated with the XmlTypeAttribute. The default is the same as the XmlSerializer (<[TypeName]Of[GenericTypeArgs]/>).
WithDefaultGenericListXmlNameFormat(string listNameFormat) This is the format of generic xml list element names. The default is the same as the XmlSerializer (<ArrayOf[GenericTypeArgs]/>).

The following are the serialization configuration options:

PrettyPrintXml() Indent the xml and make it readable.
ExcludeNullValues() Do not serialize the nodes of properties that are null.
XmlValuesAsAttributes() Specifies that values are serialized as xml attributes instead of elements.
WithDefaultXmlNamespace(string namespace) Specifies the default xml namespace.
AddXmlNamespace(string prefix, string namespace) Adds an xml namespace and prefix.
AddWriter<T>(Func<WriterContext<T>> writter) Allows you to override how a value of a specific type is serialized.
AddWriter<T>(Func<WriterContext<T>> writter,
              bool handleNullable) where T : struct
Allows you to override how both the nullable and non-nullable value of a specific type is serialized.
AddWriter(Action<WriterContext> writter) Allows you to override xml elements, xml attributes and json fields.
AddWriter(Func<WriterContext, bool> predicate,
           Action<WriterContext> writter)
Allows you to override xml elements, xml attributes and json fields that match the specified criteria.

The following are the deserialization configuration options:

DefaultNonNullableTypesWhenEmpty() Set the property to the default value when the element is empty and the type is non nullable.
IgnoreUnmatchedNodes() Ignore elements in the source xml or json that don't match properties in the target object. By default an exception is thrown if unmatched nodes exist.
IgnoreTypeXmlElementNames() Ignore type xml element names in the source xml that don't match the type xml name. This applies specifically to the root element and list elements. In these two cases the element name is based on the type xml name. By default an exception is thrown if the element name does not match the type xml name.
IgnoreCase() Ignore the case of the node name when deserializing.
AddReader<T>(Func<ReaderContext, T> reader) Allows you to override how a value is deserialized.
AddReader<T>(Func<ReaderContext, T> reader,
              bool handleNullable) where T : struct
Allows you to override how both the nullable and non-nullable value is deserialized.
WithFriendlyParseErrorMessage<T>(string message) Allows you to override friendly error messages returned when a value cannot be parsed.

Props

Thanks to JetBrains for providing OSS licenses!

About

Xml and json serializer for .NET and Mono

Resources

License

Stars

Watchers

Forks

Packages

No packages published