Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Use XmlWriter to serialize TwiML instead of using XDocument #669

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Twilio/TwiML/Fax/Receive.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public Receive(Uri action = null,
/// <summary>
/// Return the attributes of the TwiML tag
/// </summary>
protected override List<XAttribute> GetElementAttributes()
protected override IEnumerable<XAttribute> GetElementAttributes()
{
var attributes = new List<XAttribute>();
if (this.Action != null)
Expand Down
2 changes: 1 addition & 1 deletion src/Twilio/TwiML/Messaging/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ protected override string GetElementBody()
/// <summary>
/// Return the attributes of the TwiML tag
/// </summary>
protected override List<XAttribute> GetElementAttributes()
protected override IEnumerable<XAttribute> GetElementAttributes()
{
var attributes = new List<XAttribute>();
if (this.To != null)
Expand Down
2 changes: 1 addition & 1 deletion src/Twilio/TwiML/Messaging/Redirect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ protected override string GetElementBody()
/// <summary>
/// Return the attributes of the TwiML tag
/// </summary>
protected override List<XAttribute> GetElementAttributes()
protected override IEnumerable<XAttribute> GetElementAttributes()
{
var attributes = new List<XAttribute>();
if (this.Method != null)
Expand Down
22 changes: 20 additions & 2 deletions src/Twilio/TwiML/Text.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using System.Xml.Linq;
#if !NET35
using System.Threading;
using System.Threading.Tasks;
#endif
using System.Xml;
using System.Xml.Linq;

namespace Twilio.TwiML
{
Expand All @@ -15,5 +20,18 @@ protected override XNode ToXml()
{
return new XText(_content);
}

#if !NET35
protected override async Task WriteXml(XmlWriter writer, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
await writer.WriteStringAsync(_content).ConfigureAwait(false);
}
#endif

protected override void WriteContent(XmlWriter writer)
{
writer.WriteString(_content);
}
}
}
}
110 changes: 17 additions & 93 deletions src/Twilio/TwiML/TwiML.cs
Original file line number Diff line number Diff line change
@@ -1,44 +1,38 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Twilio.Exceptions;

namespace Twilio.TwiML
{

/// <summary>
/// Base class for all TwiML Objects.
/// </summary>
public class TwiML
public partial class TwiML
{
/// <summary>
/// Tag name
/// </summary>
private string TagName { get; }

/// <summary>
/// Children elements
/// </summary>
private List<TwiML> Children { get; }

/// <summary>
/// Additional tag attributes to set on the generated xml
/// </summary>
private List<KeyValuePair<string,string>> Options { get; }
/// <summary>
/// Attribute names to be transformed on the generated xml
/// </summary>
private Dictionary<string, string> AttributeNameMapper = new Dictionary<string, string> { { "for_", "for" } };
private List<KeyValuePair<string, string>> Options { get; }

/// <summary>
/// Base constructor to create any TwiML instance.
/// </summary>
/// <param name="tagName"> TwiML tag name </param>
protected TwiML(string tagName)
{
this.TagName = tagName;
this.Children = new List<TwiML>();
this.Options = new List<KeyValuePair<string,string>>();
TagName = tagName;
Children = new List<TwiML>();
Options = new List<KeyValuePair<string, string>>();
}

/// <summary>
Expand All @@ -52,9 +46,9 @@ protected virtual string GetElementBody()
/// <summary>
/// Get the TwiML element attributes.
/// </summary>
protected virtual List<XAttribute> GetElementAttributes()
protected virtual IEnumerable<XAttribute> GetElementAttributes()
{
return new List<XAttribute>();
return Enumerable.Empty<XAttribute>();
}

/// <summary>
Expand All @@ -63,13 +57,13 @@ protected virtual List<XAttribute> GetElementAttributes()
/// <param name="childElem"> Child TwiML element to add </param>
public virtual TwiML Append(TwiML childElem)
{
this.Children.Add(childElem);
Children.Add(childElem);
return this;
}

public TwiML AddText(string text)
{
this.Children.Add(new Text(text));
Children.Add(new Text(text));
return this;
}

Expand All @@ -79,7 +73,7 @@ public TwiML AddText(string text)
/// <param name="childElem"> Child TwiML element to add </param>
public T Nest<T>(T childElem) where T : TwiML
{
this.Children.Add(childElem);
Children.Add(childElem);
return childElem;
}

Expand All @@ -89,7 +83,7 @@ public T Nest<T>(T childElem) where T : TwiML
/// <param name="tagName"> TwiML tag name </param>
public TwiML AddChild(string tagName)
{
return this.Nest(new TwiML(tagName));
return Nest(new TwiML(tagName));
}

/// <summary>
Expand All @@ -99,12 +93,12 @@ public TwiML AddChild(string tagName)
/// <param name="value"> Option value </param>
public TwiML SetOption(string key, object value)
{
if (this.Options.Exists(e => e.Key.Equals(key)))
if (Options.Exists(e => e.Key.Equals(key)))
{
this.Options.Remove(this.Options.Find(e => e.Key.Equals(key)));
Options.Remove(Options.Find(e => e.Key.Equals(key)));
}

this.Options.Add(new KeyValuePair<string, string>(key, value.ToString()));
Options.Add(new KeyValuePair<string, string>(key, value.ToString()));
return this;
}

Expand All @@ -114,77 +108,7 @@ public TwiML SetOption(string key, object value)
/// <param name="key"> Option key </param>
public virtual string GetOption(string key)
{
return this.Options.Find(e => e.Key.Equals(key)).Value;
}

/// <summary>
/// Generate XElement from TwiML object
/// </summary>
protected virtual XNode ToXml()
{
var elem = new XElement(this.TagName, this.GetElementBody());

this.GetElementAttributes().ForEach(attr =>
{
string transformedAttr = attr.Name.LocalName;
if (AttributeNameMapper.TryGetValue(attr.Name.LocalName, out transformedAttr))
{
elem.Add(new XAttribute(transformedAttr, attr.Value));
}
else
{
elem.Add(attr);
}
});

this.Options.ForEach(e =>
{
if (e.Key.StartsWith("xml:"))
{
var attribute = new XAttribute(XNamespace.Xml + e.Key.Substring(4), e.Value);
elem.Add(attribute);
}
else
{
elem.Add(new XAttribute(e.Key, e.Value));
}
});

this.Children.ForEach(child => elem.Add(child.ToXml()));

return elem;
}

/// <summary>
/// Generate XDocument from TwiML object
/// </summary>
public XDocument ToXDocument()
{
var declaration = new XDeclaration("1.0", "utf-8", null);
var elem = this.ToXml();
var document = new XDocument(declaration, elem);
return document;
}

/// <summary>
/// Generate XML string from TwiML object
/// </summary>
/// <param name="formattingOptions"> Change generated string format. </param>
public string ToString(SaveOptions formattingOptions = SaveOptions.None)
{
var document = this.ToXDocument();
var writer = new Utf8StringWriter();
document.Save(writer, formattingOptions);
return writer.GetStringBuilder().ToString();
return Options.Find(e => e.Key.Equals(key)).Value;
}
}

/// <summary>
/// StringWriter which overrides default encoding to use UTF8.
/// </summary>
public class Utf8StringWriter : StringWriter
{
public override Encoding Encoding => Encoding.UTF8;
}

}
Loading