Skip to content

Commit

Permalink
fix xml serialization (#3455)
Browse files Browse the repository at this point in the history
* * update xml serialization tests to use all of the scenario folders
* Fix XmlSerialization breaks which crept in because serialization test was pinned to v1.0 scenario files
* Fixed xml serialization of nullable types

* fix truly strange implementation of AdaptiveHeight "struct"

* add copyright
  • Loading branch information
Tom Laird-McConnell authored and shalinijoshi19 committed Sep 20, 2019
1 parent 5211c46 commit 3ca37b1
Show file tree
Hide file tree
Showing 17 changed files with 246 additions and 113 deletions.
1 change: 1 addition & 0 deletions source/dotnet/Library/AdaptiveCards/AdaptiveActionSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class AdaptiveActionSet : AdaptiveElement
[XmlElement(typeof(AdaptiveShowCardAction))]
[XmlElement(typeof(AdaptiveSubmitAction))]
[XmlElement(typeof(AdaptiveToggleVisibilityAction))]
[XmlElement(typeof(AdaptiveUnknownAction))]
#endif
public List<AdaptiveAction> Actions { get; set; } = new List<AdaptiveAction>();
}
Expand Down
28 changes: 6 additions & 22 deletions source/dotnet/Library/AdaptiveCards/AdaptiveCard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,9 @@ public AdaptiveCard() : this(new AdaptiveSchemaVersion(1, 0)) { }
[JsonConverter(typeof(StringSizeWithUnitConverter), true)]
[JsonProperty(Order = -4, DefaultValueHandling = DefaultValueHandling.Ignore)]
#if !NETSTANDARD1_3
[XmlElement(typeof(AdaptiveHeight))]
[XmlElement]
#endif
[DefaultValue(typeof(AdaptiveHeight), "auto")]
public AdaptiveHeight Height { get; set; }
public AdaptiveHeight Height { get; set; } = new AdaptiveHeight(AdaptiveHeightType.Auto);

/// <summary>
/// Explicit card minimum height in pixels
Expand All @@ -146,6 +145,7 @@ public AdaptiveCard() : this(new AdaptiveSchemaVersion(1, 0)) { }
[JsonConverter(typeof(IgnoreEmptyItemsConverter<AdaptiveElement>))]
#if !NETSTANDARD1_3
[XmlElement(typeof(AdaptiveTextBlock))]
[XmlElement(typeof(AdaptiveRichTextBlock))]
[XmlElement(typeof(AdaptiveImage))]
[XmlElement(typeof(AdaptiveContainer))]
[XmlElement(typeof(AdaptiveColumnSet))]
Expand All @@ -159,6 +159,7 @@ public AdaptiveCard() : this(new AdaptiveSchemaVersion(1, 0)) { }
[XmlElement(typeof(AdaptiveChoiceSetInput))]
[XmlElement(typeof(AdaptiveMedia))]
[XmlElement(typeof(AdaptiveActionSet))]
[XmlElement(typeof(AdaptiveUnknownElement))]
#endif
public List<AdaptiveElement> Body { get; set; } = new List<AdaptiveElement>();

Expand All @@ -174,6 +175,7 @@ public AdaptiveCard() : this(new AdaptiveSchemaVersion(1, 0)) { }
[XmlElement(typeof(AdaptiveShowCardAction))]
[XmlElement(typeof(AdaptiveSubmitAction))]
[XmlElement(typeof(AdaptiveToggleVisibilityAction))]
[XmlElement(typeof(AdaptiveUnknownAction))]
#endif
public List<AdaptiveAction> Actions { get; set; } = new List<AdaptiveAction>();

Expand Down Expand Up @@ -213,25 +215,7 @@ public bool ShouldSerializeJsonSchema()
[DefaultValue(null)]
public AdaptiveAction SelectAction { get; set; }

public bool ShouldSerializeHeight()
{
if (Height == AdaptiveHeight.Auto)
{
return false;
}
if (Height.HeightType == AdaptiveHeightType.Pixel)
{
if (!Height.Unit.HasValue)
{
return false;
}
if (Height.Unit.Value == 0)
{
return false;
}
}
return true;
}
public bool ShouldSerializeHeight() => this.Height?.ShouldSerializeAdaptiveHeight() == true;

/// <summary>
/// Callback that will be invoked should a null or empty version string is encountered. The callback may return an alternate version to use for parsing.
Expand Down
16 changes: 12 additions & 4 deletions source/dotnet/Library/AdaptiveCards/AdaptiveCollectionElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,24 +16,32 @@ namespace AdaptiveCards
/// </summary>
public abstract class AdaptiveCollectionElement : AdaptiveElement
{

/// <summary>
/// The style in which the image is displayed.
/// </summary>
[JsonConverter(typeof(IgnoreNullEnumConverter<AdaptiveContainerStyle>), true)]
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
#if !NETSTANDARD1_3
[XmlElement]
[XmlIgnore]
#endif
[DefaultValue(null)]
public AdaptiveContainerStyle? Style { get; set; }

#if !NETSTANDARD1_3
// Xml Serializer doesn't handle nullable value types, but this trick allows us to serialize only if non-null
[JsonIgnore]
[XmlAttribute("Style")]
[EditorBrowsable(EditorBrowsableState.Never)]
public AdaptiveContainerStyle StyleXml { get { return (Style.HasValue) ? Style.Value : AdaptiveContainerStyle.Default; } set { Style = value; } }
public bool ShouldSerializeStyleXml() => this.Style.HasValue;
#endif

/// <summary>
/// The content alignment for the element inside the container.
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
#if !NETSTANDARD1_3
[XmlElement]
[XmlAttribute]
#endif
[DefaultValue(typeof(AdaptiveVerticalContentAlignment), "top")]
public AdaptiveVerticalContentAlignment VerticalContentAlignment { get; set; }
Expand All @@ -53,7 +61,7 @@ public abstract class AdaptiveCollectionElement : AdaptiveElement
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
#if !NETSTANDARD1_3
[XmlElement]
[XmlAttribute]
#endif
[DefaultValue(false)]
public bool Bleed { get; set; }
Expand Down
2 changes: 2 additions & 0 deletions source/dotnet/Library/AdaptiveCards/AdaptiveContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public class AdaptiveContainer : AdaptiveCollectionElement
[JsonConverter(typeof(IgnoreEmptyItemsConverter<AdaptiveElement>))]
#if !NETSTANDARD1_3
[XmlElement(typeof(AdaptiveTextBlock))]
[XmlElement(typeof(AdaptiveRichTextBlock))]
[XmlElement(typeof(AdaptiveImage))]
[XmlElement(typeof(AdaptiveContainer))]
[XmlElement(typeof(AdaptiveColumnSet))]
Expand All @@ -47,6 +48,7 @@ public class AdaptiveContainer : AdaptiveCollectionElement
[XmlElement(typeof(AdaptiveToggleInput))]
[XmlElement(typeof(AdaptiveMedia))]
[XmlElement(typeof(AdaptiveActionSet))]
[XmlElement(typeof(AdaptiveUnknownElement))]
#endif
public List<AdaptiveElement> Items { get; set; } = new List<AdaptiveElement>();

Expand Down
27 changes: 4 additions & 23 deletions source/dotnet/Library/AdaptiveCards/AdaptiveElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,17 @@ public abstract class AdaptiveElement : AdaptiveTypedElement
[Obsolete("CardElement.Speak has been deprecated. Use AdaptiveCard.Speak", false)]
public string Speak { get; set; }

public bool ShouldSerializeHeight()
{
if (Height == AdaptiveHeight.Auto)
{
return false;
}
if (Height.HeightType == AdaptiveHeightType.Pixel)
{
if (!Height.Unit.HasValue)
{
return false;
}
if (Height.Unit.Value == 0)
{
return false;
}
}
return true;
}

/// <summary>
/// The amount of space the element should be separated from the previous element. Default value is <see cref="AdaptiveHeight.Default"/>.
/// </summary>
[JsonConverter(typeof(StringSizeWithUnitConverter), true)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
#if !NETSTANDARD1_3
[XmlElement(typeof(AdaptiveHeight))]
[XmlElement]
#endif
[DefaultValue(typeof(AdaptiveHeight), "auto")]
public AdaptiveHeight Height { get; set; }
public AdaptiveHeight Height { get; set; } = new AdaptiveHeight(AdaptiveHeightType.Auto);

public bool ShouldSerializeHeight() => this.Height?.ShouldSerializeAdaptiveHeight() == true;

/// <summary>
/// Indicates whether the element should be visible when the card has been rendered.
Expand Down
74 changes: 43 additions & 31 deletions source/dotnet/Library/AdaptiveCards/AdaptiveHeight.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
using System;
using System.Xml.Serialization;

namespace AdaptiveCards
{
Expand Down Expand Up @@ -28,30 +30,48 @@ public enum AdaptiveHeightType
}


public struct AdaptiveHeight
public class AdaptiveHeight : IEquatable<AdaptiveHeight>
{
public static AdaptiveHeight Auto { get; } = new AdaptiveHeight(AdaptiveHeightType.Auto);

public static AdaptiveHeight Stretch { get; } = new AdaptiveHeight(AdaptiveHeightType.Stretch);

public AdaptiveHeightType _heightType;
public AdaptiveHeightType HeightType { get { return _heightType; } set { } }

public uint? _unit;
public uint? Unit { get { return _unit; } set { } }
public AdaptiveHeight()
{
}

public AdaptiveHeight(uint px)
{
_heightType = AdaptiveHeightType.Pixel;
_unit = px;
HeightType = AdaptiveHeightType.Pixel;
this.Unit = px;
}

public AdaptiveHeight(AdaptiveHeightType heightType)
{
_heightType = heightType;
_unit = null;
HeightType = heightType;
Unit = null;
}


[JsonProperty("heightType")]
#if !NETSTANDARD1_3
[XmlAttribute]
#endif
public AdaptiveHeightType HeightType { get; set; }

[JsonProperty("unit")]
#if !NETSTANDARD1_3
[XmlIgnore]
#endif
public uint? Unit { get; set; }

#if !NETSTANDARD1_3
[XmlAttribute("Unit")]
[JsonIgnore]
public uint UnitXml { get { return Unit.HasValue ? Unit.Value : 0; } set { Unit = value; } }
public bool ShouldSerializeUnitXml() => Unit.HasValue;
#endif

public bool IsPixel()
{
return HeightType == AdaptiveHeightType.Pixel;
Expand All @@ -63,7 +83,8 @@ public bool ShouldSerializeAdaptiveHeight()
{
return false;
}
if( HeightType == AdaptiveHeightType.Pixel )

if (HeightType == AdaptiveHeightType.Pixel)
{
if (!Unit.HasValue)
{
Expand All @@ -79,23 +100,13 @@ public bool ShouldSerializeAdaptiveHeight()

public static bool operator ==(AdaptiveHeight ah1, AdaptiveHeight ah2)
{
if (ah1 != null && ah2 != null)
{
return ah1.Equals(ah2);
}

if (ah1 == null && ah2 == null)
{
return true;
}
return false;
return ah1.Equals(ah2);
}

public static bool operator !=(AdaptiveHeight ah1, AdaptiveHeight ah2)
{
return !(ah1 == ah2);
return !ah1.Equals(ah2);
}

public override int GetHashCode()
{
if (!Unit.HasValue)
Expand All @@ -107,17 +118,18 @@ public override int GetHashCode()

public override bool Equals(object obj)
{
if (obj is AdaptiveHeight)
return this.Equals(obj as AdaptiveHeight);
}

public Boolean Equals(AdaptiveHeight other)
{
if (this.HeightType == other.HeightType)
{
AdaptiveHeight ah = (AdaptiveHeight)obj;
if (HeightType == ah.HeightType)
if (this.HeightType == AdaptiveHeightType.Pixel)
{
if (HeightType == AdaptiveHeightType.Pixel)
{
return Unit == ah.Unit;
}
return true;
return this.Unit == other.Unit;
}
return true;
}
return false;
}
Expand Down
37 changes: 37 additions & 0 deletions source/dotnet/Library/AdaptiveCards/AdaptiveInline.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace AdaptiveCards
{
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public abstract class AdaptiveInline
{
/// <summary>
/// The type name of the inline
/// </summary>
[JsonProperty(Order = -10, Required = Required.Always, DefaultValueHandling = DefaultValueHandling.Include)]
#if !NETSTANDARD1_3
// don't serialize type with xml, because we use element name or attribute for type
[XmlIgnore]
#endif
public abstract string Type { get; set; }

/// <summary>
/// Additional properties not found on the default schema
/// </summary>
[JsonExtensionData]
#if NETSTANDARD1_3
public IDictionary<string, object> AdditionalProperties { get; set; } = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
#else
// Dictionary<> is not supported with XmlSerialization because Dictionary is not serializable, SerializableDictionary<> is
[XmlElement]
public SerializableDictionary<string, object> AdditionalProperties { get; set; } = new SerializableDictionary<string, object>(StringComparer.OrdinalIgnoreCase);
public bool ShouldSerializeAdditionalProperties() => this.AdditionalProperties.Count > 0;
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ class AdaptiveInlinesConverter : JsonConverter

public override bool CanConvert(Type objectType)
{
return typeof(List<IAdaptiveInline>).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
return typeof(List<AdaptiveInline>).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var array = JArray.Load(reader);
List<object> list = array.ToObject<List<object>>();
List<IAdaptiveInline> arrayList = new List<IAdaptiveInline>();
List<AdaptiveInline> arrayList = new List<AdaptiveInline>();

// We only support text runs for now, which can be specified as either a string or an object
foreach (object obj in list)
Expand All @@ -35,7 +35,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
else
{
JObject jobj = (JObject)obj;
arrayList.Add((IAdaptiveInline)jobj.ToObject(typeof(AdaptiveTextRun)));
arrayList.Add((AdaptiveInline)jobj.ToObject(typeof(AdaptiveTextRun)));
}
}
return arrayList;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,6 @@ public AdaptiveRichTextBlock()
#if !NETSTANDARD1_3
[XmlElement(typeof(AdaptiveTextRun))]
#endif
public List<IAdaptiveInline> Inlines { get; set; } = new List<IAdaptiveInline>();
public List<AdaptiveInline> Inlines { get; set; } = new List<AdaptiveInline>();
}
}
Loading

0 comments on commit 3ca37b1

Please sign in to comment.