Skip to content

Commit

Permalink
The .NET DefaultValue attribute is now respected
Browse files Browse the repository at this point in the history
- Properties with the .NET component model DefaultValue and matching
  values will no longer be serialized unless EmitDefaults is on
  (previously only properties with null or zero were ignored).
- Moved the default exclusion mechanism from
  DefaultExclusiveObjectGraphVisitor to the traversal strategy, since
  visitors have no access to property attributes (it seems more correct
  as well).
  • Loading branch information
roji authored and Shay Rojansky committed May 5, 2013
1 parent 9335f00 commit 9fe1e94
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 38 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
Expand All @@ -15,6 +16,7 @@ public class FullObjectGraphTraversalStrategy : IObjectGraphTraversalStrategy
{
protected readonly Serializer serializer;
private readonly int maxRecursion;
public bool EmitDefaults { get; set; }

public FullObjectGraphTraversalStrategy(Serializer serializer, int maxRecursion)
{
Expand Down Expand Up @@ -87,7 +89,7 @@ protected virtual void Traverse(object value, Type type, IObjectGraphVisitor vis
}

// This is a nullable type, recursively handle it with its underlying type.
// Not that if it contains null, the condition above already took care of it
// Note that if it contains null, the condition above already took care of it
Traverse(value, underlyingType, visitor, currentDepth);
break;
}
Expand Down Expand Up @@ -186,12 +188,26 @@ protected virtual void SerializeProperties(object value, Type type, IObjectGraph
{
visitor.VisitMappingStart(value, type, typeof(string), typeof(object));

var props = TypeDescriptor.GetProperties(type);

foreach (var property in GetTraversableProperties(type))
{
var propertyValue = property.GetValue(value, null);
var propertyType = property.PropertyType;
var propertyName = GetPropertyName(type, property);

if (!EmitDefaults)
{
if ((propertyType.IsValueType && propertyValue == Activator.CreateInstance(propertyType)) || propertyValue == null)
continue;

var defaultAttr = (DefaultValueAttribute)props[property.Name].Attributes[typeof (DefaultValueAttribute)];

if (defaultAttr != null && propertyValue.Equals(defaultAttr.Value))
continue;
}


if(visitor.EnterMapping(propertyName, typeof(string), propertyValue, propertyType))
{
Traverse(propertyName, typeof(string), visitor, currentDepth);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ public interface IObjectGraphTraversalStrategy
/// <param name="type">The static type of the graph.</param>
/// <param name="visitor">An <see cref="IObjectGraphVisitor"/> that is to be notified during the traversal.</param>
void Traverse(object graph, Type type, IObjectGraphVisitor visitor);

/// <summary>
/// Defines whether the traversal should include elements which contain the default value (e.g. null)
/// </summary>
bool EmitDefaults { get; set; }
}
}
15 changes: 8 additions & 7 deletions YamlDotNet.RepresentationModel/Serialization/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,6 @@ private IObjectGraphVisitor CreateEmittingVisitor(Emitter emitter, Serialization
emittingVisitor = new AnchorAssigningObjectGraphVisitor(emittingVisitor, eventEmitter, anchorAssigner);
}

if ((options & SerializationOptions.EmitDefaults) == 0)
{
emittingVisitor = new DefaultExclusiveObjectGraphVisitor(emittingVisitor);
}

return emittingVisitor;
}

Expand All @@ -194,14 +189,20 @@ private IEventEmitter CreateEventEmitter(Emitter emitter, SerializationOptions o

private IObjectGraphTraversalStrategy CreateTraversalStrategy(SerializationOptions options)
{
IObjectGraphTraversalStrategy strategy;

if ((options & SerializationOptions.Roundtrip) != 0)
{
return new RoundtripObjectGraphTraversalStrategy(this, 50);
strategy = new RoundtripObjectGraphTraversalStrategy(this, 50);
}
else
{
return new FullObjectGraphTraversalStrategy(this, 50);
strategy = new FullObjectGraphTraversalStrategy(this, 50);
}

strategy.EmitDefaults = (options & SerializationOptions.EmitDefaults) != 0;

return strategy;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
<Compile Include="Serialization\ChainedEventEmitter.cs" />
<Compile Include="Serialization\ChainedObjectGraphVisitor.cs" />
<Compile Include="Serialization\CustomSerializationObjectGraphVisitor.cs" />
<Compile Include="Serialization\DefaultExclusiveObjectGraphVisitor.cs" />
<Compile Include="Serialization\DefaultObjectFactory.cs" />
<Compile Include="Serialization\DeserializationContext.cs" />
<Compile Include="Serialization\DeserializationOptions.cs" />
Expand Down
17 changes: 17 additions & 0 deletions YamlDotNet.UnitTests/RepresentationModel/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public string Nothing

private int myInt = 1234;

[DefaultValue(666)]
public int MyInt
{
get
Expand Down Expand Up @@ -769,6 +770,22 @@ public void DeserializationOfNullWorksInJson()
}
}

[Fact]
public void DefaultValueAttributeIsRespected()
{
var serializer = new Serializer();

using (StringWriter buffer = new StringWriter())
{
X original = new X { MyInt = 666 };
serializer.Serialize(buffer, original, typeof(X));

Console.WriteLine(buffer.ToString());

Assert.False(buffer.ToString().Contains("MyInt"));
}
}

//[Fact]
//public void DeserializationIgnoresUnknownProperties()
//{
Expand Down

0 comments on commit 9fe1e94

Please sign in to comment.