Skip to content

Commit

Permalink
Handle XmlArrayItemAttribute #3.
Browse files Browse the repository at this point in the history
  • Loading branch information
JohanLarsson committed Sep 14, 2018
1 parent 4db074d commit 7ca49d8
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 38 deletions.
4 changes: 2 additions & 2 deletions Gu.Xml.Tests/XmlTests.SerializeWithXmlAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,14 @@ public class FieldWithXmlElementAttributeExplicitName
public class WithXmlArrayOfIntAndXmlArrayItemAttribute
{
[XmlArray(ElementName = "Numbers")]
[XmlArrayItem(ElementName = "Int32")]
[XmlArrayItem(ElementName = "Number")]
public int[] Values { get; set; }
}

public class WithXmlArrayOfObjectAndXmlArrayItemAttribute
{
[XmlArray(ElementName = "Numbers")]
[XmlArrayItem(ElementName = "Int32")]
[XmlArrayItem(ElementName = "Number")]
public object[] Values { get; set; }
}

Expand Down
24 changes: 12 additions & 12 deletions Gu.Xml/Internals/FieldOrProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,49 @@
using System.Diagnostics;
using System.Reflection;

[DebuggerDisplay("{this.SourceType}{this.member.Name} ({this.ValueType})")]
[DebuggerDisplay("{this.SourceType}{this.MemberInfo.Name} ({this.ValueType})")]
internal struct FieldOrProperty
{
private readonly MemberInfo member;
internal readonly MemberInfo MemberInfo;

public FieldOrProperty(FieldInfo member)
internal FieldOrProperty(FieldInfo memberInfo)
{
this.member = member;
this.MemberInfo = memberInfo;
}

public FieldOrProperty(PropertyInfo member)
internal FieldOrProperty(PropertyInfo memberInfo)
{
this.member = member;
this.MemberInfo = memberInfo;
}

public Type SourceType => this.member.ReflectedType;
internal Type SourceType => this.MemberInfo.ReflectedType;

public Type ValueType
internal Type ValueType
{
get
{
switch (this.member)
switch (this.MemberInfo)
{
case FieldInfo field:
return field.FieldType;
case PropertyInfo property:
return property.PropertyType;
default:
throw new ArgumentOutOfRangeException(nameof(this.member), "Never getting here.");
throw new ArgumentOutOfRangeException(nameof(this.MemberInfo), "Never getting here.");
}
}
}

public Delegate CreateGetter()
{
switch (this.member)
switch (this.MemberInfo)
{
case FieldInfo field:
return field.CreateGetter();
case PropertyInfo property:
return property.CreateGetter();
default:
throw new ArgumentOutOfRangeException(nameof(this.member), "Never getting here.");
throw new ArgumentOutOfRangeException(nameof(this.MemberInfo), "Never getting here.");
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions Gu.Xml/Writers/WriteMaps/ComplexWriteMap.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace Gu.Xml
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -188,6 +189,16 @@ private static bool TryCreate(FieldOrProperty fieldOrProperty, string name, Writ
return true;
}

if (ItemsWriteMap.TryCreate(fieldOrProperty, maps, out var itemsMap))
{
// ReSharper disable once PossibleNullReferenceException
castAction = (CastAction<XmlWriter>)typeof(ElementAction)
.GetMethod(nameof(CreateItemsCached), BindingFlags.Static | BindingFlags.NonPublic)
.MakeGenericMethod(fieldOrProperty.SourceType, fieldOrProperty.ValueType)
.Invoke(null, new object[] { name, fieldOrProperty.CreateGetter(), itemsMap });
return true;
}

// ReSharper disable once PossibleNullReferenceException
castAction = (CastAction<XmlWriter>)typeof(ElementAction)
.GetMethod(nameof(Create), BindingFlags.Static | BindingFlags.NonPublic)
Expand Down Expand Up @@ -220,6 +231,18 @@ private static CastAction<XmlWriter> CreateComplexCached<TSource, TValue>(string
});
}

private static CastAction<XmlWriter> CreateItemsCached<TSource, TValue>(string name, Func<TSource, TValue> getter, ItemsWriteMap writeMap)
{
return CastAction<XmlWriter>.Create<TSource>((writer, source) =>
{
if (getter(source) is TValue value)
{
writer.WriteElement(name, value, writeMap);
writer.WriteLine();
}
});
}

private static CastAction<XmlWriter> Create<TSource, TValue>(string name, Func<TSource, TValue> getter)
{
return CastAction<XmlWriter>.Create<TSource>((writer, source) =>
Expand Down
37 changes: 34 additions & 3 deletions Gu.Xml/Writers/WriteMaps/ItemsWriteMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,54 @@ private ItemsWriteMap(CastAction<XmlWriter> write)
this.Write = write;
}

internal static ItemsWriteMap Create(Type type, WriteMaps maps, string elementName)
internal static bool TryCreate(FieldOrProperty member, WriteMaps maps, out ItemsWriteMap map)
{
if (typeof(IEnumerable).IsAssignableFrom(member.ValueType))
{
if (member.MemberInfo.TryGetCustomAttribute<System.Xml.Serialization.XmlArrayItemAttribute>(
out var attribute) &&
attribute.ElementName is string elementName &&
!string.IsNullOrEmpty(elementName))
{
return TryCreate(member.ValueType, maps, elementName, out map);
}

return TryCreate(member.ValueType, maps, null, out map);
}

map = null;
return false;
}

internal static ItemsWriteMap Create(Type type, WriteMaps maps)
{
if (type.IsArray &&
type.GetArrayRank() > 1)
{
throw new NotSupportedException("Multidimensional arrays are not yet supported. Issue #26.");
}

if (DictionaryMap.TryCreate(type, maps, elementName, out var map) ||
EnumerableMap.TryCreate(type, maps, elementName, out map))
if (TryCreate(type, maps, null, out var map))
{
return map;
}

throw new InvalidOperationException("Failed creating ItemsWriteMap. Bug in Gu.Xml.");
}

private static bool TryCreate(Type type, WriteMaps maps, string elementName, out ItemsWriteMap map)
{
if (type.IsArray &&
type.GetArrayRank() > 1)
{
map = null;
return false;
}

return DictionaryMap.TryCreate(type, maps, elementName, out map) ||
EnumerableMap.TryCreate(type, maps, elementName, out map);
}

private static ItemsWriteMap Create<T>(Action<XmlWriter, T> writeItems)
{
return new ItemsWriteMap(CastAction<XmlWriter>.Create(writeItems));
Expand Down
2 changes: 1 addition & 1 deletion Gu.Xml/Writers/WriteMaps/WriteMaps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ private bool TryGetItems<T>(T value, out ItemsWriteMap map)

ItemsWriteMap Create(Type x)
{
return ItemsWriteMap.Create(x, this, null);
return ItemsWriteMap.Create(x, this);
}
}

Expand Down
45 changes: 25 additions & 20 deletions Gu.Xml/Writers/XmlWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,26 +86,7 @@ public void WriteElement<T>(string name, T value)
simple.WriteElement(this, name, value);
break;
case ItemsWriteMap items:
this.WriteIndentation();
this.TextWriter.Write("<");
this.TextWriter.Write(name);
this.pendingCloseStartElement = true;

this.indentLevel++;
items.Write.Invoke(this, value);
this.indentLevel--;

if (this.pendingCloseStartElement)
{
this.TextWriter.Write(" />");
this.pendingCloseStartElement = false;
}
else
{
this.WriteIndentation();
this.TextWriter.WriteMany("</", name, ">");
}

this.WriteElement(name, value, items);
break;
case ComplexWriteMap complex:
this.WriteElement(name, value, complex);
Expand Down Expand Up @@ -137,6 +118,30 @@ internal void ClosePendingStart()
}
}

internal void WriteElement<T>(string name, T value, ItemsWriteMap items)
{
this.ClosePendingStart();
this.WriteIndentation();
this.TextWriter.Write("<");
this.TextWriter.Write(name);
this.pendingCloseStartElement = true;

this.indentLevel++;
items.Write.Invoke(this, value);
this.indentLevel--;

if (this.pendingCloseStartElement)
{
this.TextWriter.Write(" />");
this.pendingCloseStartElement = false;
}
else
{
this.WriteIndentation();
this.TextWriter.WriteMany("</", name, ">");
}
}

internal void WriteElement<T>(string name, T value, ComplexWriteMap writeMap)
{
this.ClosePendingStart();
Expand Down

0 comments on commit 7ca49d8

Please sign in to comment.