Skip to content

Commit

Permalink
Fix serialization of qualified attribute values
Browse files Browse the repository at this point in the history
  • Loading branch information
janno-p committed Jan 5, 2021
1 parent 2d8f735 commit ec85974
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 34 deletions.
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#### 1.4.2 - 05.01.2021

* Remove usage of Synchronous API in serialization of qualified attribute values.

#### 1.4.1 - 13.12.2020

* Review UTF8 encoding usages to prevent BOM in outgoing messages.
Expand Down
69 changes: 35 additions & 34 deletions src/XRoadLib/Extensions/XmlWriterExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,62 +19,65 @@ public static class XmlWriterExtensions
/// <summary>
/// Serializes attribute with qualified name content.
/// </summary>
public static async Task WriteQualifiedAttributeAsync(this XmlWriter writer, string name, XmlQualifiedName qualifiedName)
public static Task WriteQualifiedAttributeAsync(this XmlWriter writer, string name, XmlQualifiedName qualifiedName)
{
if (qualifiedName == null || qualifiedName.IsEmpty)
return;
return Task.CompletedTask;

var qualifiedValue = writer.GetQualifiedValue(qualifiedName.Name, qualifiedName.Namespace);

writer.WriteStartAttribute(name);
await writer.WriteQualifiedNameAsync(qualifiedName.Name, qualifiedName.Namespace).ConfigureAwait(false);
writer.WriteEndAttribute();
return writer.WriteAttributeStringAsync(null, name, null, qualifiedValue);
}

private static async Task WriteQualifiedAttributeAsync(this XmlWriter writer, string attributeName, string attributeNamespace, string valueName, string valueNamespace)
private static string GetQualifiedValue(this XmlWriter writer, string localName, string ns)
{
writer.WriteStartAttribute(attributeName, attributeNamespace);
await writer.WriteQualifiedNameAsync(valueName, valueNamespace).ConfigureAwait(false);
writer.WriteEndAttribute();
if (string.IsNullOrEmpty(ns))
return localName;

var prefix = writer.LookupPrefix(ns);
if (prefix == null)
throw new ArgumentException(nameof(ns), $"Undefined namespace: {ns}");

return $"{prefix}:{localName}";
}

private static Task WriteQualifiedAttributeAsync(this XmlWriter writer, string attributeName, string attributeNamespace, string valueName, string valueNamespace)
{
var attributePrefix = writer.LookupPrefix(attributeNamespace);
var qualifiedValue = writer.GetQualifiedValue(valueName, valueNamespace);
return writer.WriteAttributeStringAsync(attributePrefix, attributeName, attributeNamespace, qualifiedValue);
}

/// <summary>
/// Serializes xsi:type attribute.
/// </summary>
public static Task WriteTypeAttributeAsync(this XmlWriter writer, string typeName, string typeNamespace)
{
return writer.WriteQualifiedAttributeAsync(QnXsiType.Name, QnXsiType.Namespace, typeName, typeNamespace);
}
public static Task WriteTypeAttributeAsync(this XmlWriter writer, string typeName, string typeNamespace) =>
writer.WriteQualifiedAttributeAsync(QnXsiType.Name, QnXsiType.Namespace, typeName, typeNamespace);

/// <summary>
/// Serializes xsi:type attribute.
/// </summary>
public static Task WriteTypeAttributeAsync(this XmlWriter writer, XName qualifiedName, string ns = null)
{
return writer.WriteTypeAttributeAsync(qualifiedName.LocalName, ns ?? qualifiedName.NamespaceName);
}
public static Task WriteTypeAttributeAsync(this XmlWriter writer, XName qualifiedName, string ns = null) =>
writer.WriteTypeAttributeAsync(qualifiedName.LocalName, ns ?? qualifiedName.NamespaceName);

private static async Task WriteArrayTypeAttributeAsync(this XmlWriter writer, string typeName, string typeNamespace, int arraySize)
private static Task WriteArrayTypeAttributeAsync(this XmlWriter writer, string typeName, string typeNamespace, int arraySize)
{
writer.WriteStartAttribute("arrayType", NamespaceConstants.SoapEnc);
await writer.WriteQualifiedNameAsync(typeName, typeNamespace).ConfigureAwait(false);
await writer.WriteStringAsync($"[{arraySize}]").ConfigureAwait(false);
writer.WriteEndAttribute();
var soapEncPrefix = writer.LookupPrefix(NamespaceConstants.SoapEnc);
var qualifiedValue = writer.GetQualifiedValue(typeName, typeNamespace);
return writer.WriteAttributeStringAsync(soapEncPrefix, "arrayType", NamespaceConstants.SoapEnc, $"{qualifiedValue}[{arraySize}]");
}

/// <summary>
/// Serializes SOAP encoded array type attribute.
/// </summary>
public static Task WriteArrayTypeAttributeAsync(this XmlWriter writer, XName qualifiedName, int arraySize)
{
return writer.WriteArrayTypeAttributeAsync(qualifiedName.LocalName, qualifiedName.NamespaceName, arraySize);
}
public static Task WriteArrayTypeAttributeAsync(this XmlWriter writer, XName qualifiedName, int arraySize) =>
writer.WriteArrayTypeAttributeAsync(qualifiedName.LocalName, qualifiedName.NamespaceName, arraySize);

/// <summary>
/// Serializes xsi:nil element attribute.
/// </summary>
public static Task WriteNilAttributeAsync(this XmlWriter writer)
{
return writer.WriteAttributeStringAsync(null, "nil", NamespaceConstants.Xsi, "1");
}
public static Task WriteNilAttributeAsync(this XmlWriter writer) =>
writer.WriteAttributeStringAsync(null, "nil", NamespaceConstants.Xsi, "1");

private static async Task WriteCDataEscapeAsync(this XmlWriter writer, string value)
{
Expand Down Expand Up @@ -111,10 +114,8 @@ public static Task WriteStringWithModeAsync(this XmlWriter writer, string value,
: writer.WriteCDataEscapeAsync(value);
}

public static Task WriteStartElementAsync(this XmlWriter writer, XName name)
{
return writer.WriteStartElementAsync(null, name.LocalName, name.NamespaceName);
}
public static Task WriteStartElementAsync(this XmlWriter writer, XName name) =>
writer.WriteStartElementAsync(null, name.LocalName, name.NamespaceName);

/// <summary>
/// Serializes beginning of SOAP envelope into X-Road message.
Expand Down
106 changes: 106 additions & 0 deletions test/XRoadLib.Tests/XmlWriterExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
using System.IO;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using XRoadLib.Extensions;
using XRoadLib.Serialization;
using Xunit;

namespace XRoadLib.Tests
{
public class XmlWriterExtensionsTest
{
[Fact]
public async Task WriteQualifiedAttributeTest()
{
#if NET5_0
await
#endif
using var stream = new MemoryStream();

#if NET5_0
await
#endif
using var writer = XmlWriter.Create(stream, new XmlWriterSettings { Async = true, Encoding = XRoadEncoding.Utf8 });

await writer.WriteStartDocumentAsync();
await writer.WriteStartElementAsync(null, "test", null);
await writer.WriteAttributeStringAsync(PrefixConstants.Xmlns, "ppp", NamespaceConstants.Xmlns, "urn:qualifiedNamespace");
await writer.WriteQualifiedAttributeAsync("attributeName", new XmlQualifiedName("qualifiedName", "urn:qualifiedNamespace"));
await writer.WriteEndElementAsync();
await writer.WriteEndDocumentAsync();
await writer.FlushAsync();

stream.Seek(0, SeekOrigin.Begin);

var document = XDocument.Load(stream);

var attribute = document.Element(XName.Get("test"))?.Attribute("attributeName");

Assert.NotNull(attribute);
Assert.Equal("ppp:qualifiedName", attribute.Value);
}

[Fact]
public async Task WriteTypeAttributeTest()
{
#if NET5_0
await
#endif
using var stream = new MemoryStream();

#if NET5_0
await
#endif
using var writer = XmlWriter.Create(stream, new XmlWriterSettings { Async = true, Encoding = XRoadEncoding.Utf8 });

await writer.WriteStartDocumentAsync();
await writer.WriteStartElementAsync(null, "test", null);
await writer.WriteAttributeStringAsync(PrefixConstants.Xmlns, "ppp", NamespaceConstants.Xmlns, "urn:qualifiedNamespace");
await writer.WriteTypeAttributeAsync("typeName", "urn:qualifiedNamespace");
await writer.WriteEndElementAsync();
await writer.WriteEndDocumentAsync();
await writer.FlushAsync();

stream.Seek(0, SeekOrigin.Begin);

var document = XDocument.Load(stream);

var attribute = document.Element(XName.Get("test"))?.Attribute(XName.Get("type", NamespaceConstants.Xsi));

Assert.NotNull(attribute);
Assert.Equal("ppp:typeName", attribute.Value);
}

[Fact]
public async Task WriteArrayTypeAttributeTest()
{
#if NET5_0
await
#endif
using var stream = new MemoryStream();

#if NET5_0
await
#endif
using var writer = XmlWriter.Create(stream, new XmlWriterSettings { Async = true, Encoding = XRoadEncoding.Utf8 });

await writer.WriteStartDocumentAsync();
await writer.WriteStartElementAsync(null, "test", null);
await writer.WriteAttributeStringAsync(PrefixConstants.Xmlns, "ppp", NamespaceConstants.Xmlns, "urn:qualifiedNamespace");
await writer.WriteArrayTypeAttributeAsync(XName.Get("typeName", "urn:qualifiedNamespace"), 73);
await writer.WriteEndElementAsync();
await writer.WriteEndDocumentAsync();
await writer.FlushAsync();

stream.Seek(0, SeekOrigin.Begin);

var document = XDocument.Load(stream);

var attribute = document.Element(XName.Get("test"))?.Attribute(XName.Get("arrayType", NamespaceConstants.SoapEnc));

Assert.NotNull(attribute);
Assert.Equal("ppp:typeName[73]", attribute.Value);
}
}
}

0 comments on commit ec85974

Please sign in to comment.