Skip to content
Merged
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
31 changes: 21 additions & 10 deletions Stack/Opc.Ua.Types/Encoders/XmlDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ public DateTime ReadDateTime(string fieldName)
}
catch (FormatException fe)
{
throw CreateBadDecodingError(fieldName, fe);
throw CreateBadDecodingError(fieldName, fe, value: xml);
}
}
}
Expand Down Expand Up @@ -985,7 +985,7 @@ public Uuid ReadGuid(string fieldName)
}
catch (FormatException fe)
{
throw CreateBadDecodingError(fieldName, fe);
throw CreateBadDecodingError(fieldName, fe, value: guidString);
}

EndField(fieldName);
Expand Down Expand Up @@ -1092,11 +1092,11 @@ public NodeId ReadNodeId(string fieldName)
catch (ServiceResultException sre) when (sre.StatusCode == StatusCodes
.BadNodeIdInvalid)
{
throw CreateBadDecodingError(fieldName, sre);
throw CreateBadDecodingError(fieldName, sre, value: identifierText);
}
catch (ArgumentException ae)
{
throw CreateBadDecodingError(fieldName, ae);
throw CreateBadDecodingError(fieldName, ae, value: identifierText);
}

EndField(fieldName);
Expand Down Expand Up @@ -1132,11 +1132,11 @@ public ExpandedNodeId ReadExpandedNodeId(string fieldName)
catch (ServiceResultException sre) when (sre.StatusCode == StatusCodes
.BadNodeIdInvalid)
{
throw CreateBadDecodingError(fieldName, sre);
throw CreateBadDecodingError(fieldName, sre, value: identifierText);
}
catch (ArgumentException ae)
{
throw CreateBadDecodingError(fieldName, ae);
throw CreateBadDecodingError(fieldName, ae, value: identifierText);
}

EndField(fieldName);
Expand Down Expand Up @@ -1531,7 +1531,7 @@ public Enum ReadEnumerated(string fieldName, Type enumType)
}
catch (Exception ex) when (ex is ArgumentException or FormatException or OverflowException)
{
throw CreateBadDecodingError(fieldName, ex);
throw CreateBadDecodingError(fieldName, ex, value: xml);
}
}

Expand Down Expand Up @@ -3145,8 +3145,19 @@ private void CheckAndIncrementNestingLevel([CallerMemberName] string functionNam
private static ServiceResultException CreateBadDecodingError(
string fieldName,
Exception ex,
[CallerMemberName] string functionName = null)
[CallerMemberName] string functionName = null,
string value = null)
{
if (!string.IsNullOrEmpty(value))
{
return ServiceResultException.Create(
StatusCodes.BadDecodingError,
"Unable to read field {0} in function {1}: {2}. Value: '{3}'",
fieldName,
functionName,
ex.Message,
value);
}
return ServiceResultException.Create(
StatusCodes.BadDecodingError,
"Unable to read field {0} in function {1}: {2}",
Expand Down Expand Up @@ -3175,11 +3186,11 @@ private static T SafeXmlConvert<T>(
}
catch (OverflowException ove)
{
throw CreateBadDecodingError(fieldName, ove, functionName);
throw CreateBadDecodingError(fieldName, ove, functionName: functionName, value: xml);
}
catch (FormatException fe)
{
throw CreateBadDecodingError(fieldName, fe, functionName);
throw CreateBadDecodingError(fieldName, fe, functionName: functionName, value: xml);
}
}

Expand Down
92 changes: 92 additions & 0 deletions Tests/Opc.Ua.Types.Tests/Encoders/XmlEncoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,5 +260,97 @@ public void EncodeDecodeVariantNil()
// Check decode result against input value
Assert.AreEqual(actualVariant, Variant.Null);
}

/// <summary>
/// Validate that decoding errors include the failed value in the error message for Float.
/// </summary>
[Test]
public void DecodeInvalidFloatIncludesValueInError()
{
ITelemetryContext telemetry = NUnitTelemetryContext.Create();
var context = new ServiceMessageContext(telemetry);
const string invalidValue = "not-a-number";
string xmlContent = $"<?xml version=\"1.0\" encoding=\"utf-16\"?>" +
$"<FloatTest xmlns:uax=\"http://opcfoundation.org/UA/2008/02/Types.xsd\" " +
$"xmlns=\"http://opcfoundation.org/UA/2008/02/Types.xsd\">" +
$"<Value>{invalidValue}</Value></FloatTest>";

using (var reader = XmlReader.Create(new StringReader(xmlContent)))
using (var xmlDecoder = new XmlDecoder(null, reader, context))
{
var ex = Assert.Throws<ServiceResultException>(() => xmlDecoder.ReadFloat("Value"));
Assert.That(ex.Message, Does.Contain(invalidValue));
Assert.That(ex.Message, Does.Contain("Value:"));
}
}

/// <summary>
/// Validate that decoding errors include the failed value in the error message for Double.
/// </summary>
[Test]
public void DecodeInvalidDoubleIncludesValueInError()
{
ITelemetryContext telemetry = NUnitTelemetryContext.Create();
var context = new ServiceMessageContext(telemetry);
const string invalidValue = "invalid-double";
string xmlContent = $"<?xml version=\"1.0\" encoding=\"utf-16\"?>" +
$"<DoubleTest xmlns:uax=\"http://opcfoundation.org/UA/2008/02/Types.xsd\" " +
$"xmlns=\"http://opcfoundation.org/UA/2008/02/Types.xsd\">" +
$"<Value>{invalidValue}</Value></DoubleTest>";

using (var reader = XmlReader.Create(new StringReader(xmlContent)))
using (var xmlDecoder = new XmlDecoder(null, reader, context))
{
var ex = Assert.Throws<ServiceResultException>(() => xmlDecoder.ReadDouble("Value"));
Assert.That(ex.Message, Does.Contain(invalidValue));
Assert.That(ex.Message, Does.Contain("Value:"));
}
}

/// <summary>
/// Validate that decoding errors include the failed value in the error message for DateTime.
/// </summary>
[Test]
public void DecodeInvalidDateTimeIncludesValueInError()
{
ITelemetryContext telemetry = NUnitTelemetryContext.Create();
var context = new ServiceMessageContext(telemetry);
const string invalidValue = "not-a-date";
string xmlContent = $"<?xml version=\"1.0\" encoding=\"utf-16\"?>" +
$"<DateTimeTest xmlns:uax=\"http://opcfoundation.org/UA/2008/02/Types.xsd\" " +
$"xmlns=\"http://opcfoundation.org/UA/2008/02/Types.xsd\">" +
$"<Value>{invalidValue}</Value></DateTimeTest>";

using (var reader = XmlReader.Create(new StringReader(xmlContent)))
using (var xmlDecoder = new XmlDecoder(null, reader, context))
{
var ex = Assert.Throws<ServiceResultException>(() => xmlDecoder.ReadDateTime("Value"));
Assert.That(ex.Message, Does.Contain(invalidValue));
Assert.That(ex.Message, Does.Contain("Value:"));
}
}

/// <summary>
/// Validate that decoding errors include the failed value in the error message for Int32.
/// </summary>
[Test]
public void DecodeInvalidInt32IncludesValueInError()
{
ITelemetryContext telemetry = NUnitTelemetryContext.Create();
var context = new ServiceMessageContext(telemetry);
const string invalidValue = "not-an-integer";
string xmlContent = $"<?xml version=\"1.0\" encoding=\"utf-16\"?>" +
$"<Int32Test xmlns:uax=\"http://opcfoundation.org/UA/2008/02/Types.xsd\" " +
$"xmlns=\"http://opcfoundation.org/UA/2008/02/Types.xsd\">" +
$"<Value>{invalidValue}</Value></Int32Test>";

using (var reader = XmlReader.Create(new StringReader(xmlContent)))
using (var xmlDecoder = new XmlDecoder(null, reader, context))
{
var ex = Assert.Throws<ServiceResultException>(() => xmlDecoder.ReadInt32("Value"));
Assert.That(ex.Message, Does.Contain(invalidValue));
Assert.That(ex.Message, Does.Contain("Value:"));
}
}
}
}