Skip to content

Commit

Permalink
[Fix] Deserialize ValueSets ;Fixes #205
Browse files Browse the repository at this point in the history
* Fix problem and add unit test
* Move methods from ValueArrayUtils to SerializerHelper
* Fix unwanted removal of Extension Method
* Fix test Category
* Upgrade CI build to net48 instead of net452
  • Loading branch information
lxatstariongroup authored Oct 1, 2021
1 parent e3ad645 commit 8960c60
Show file tree
Hide file tree
Showing 8 changed files with 183 additions and 165 deletions.
151 changes: 1 addition & 150 deletions CDP4Common/Helpers/ValueArrayUtils.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ValueArrayUtils.cs" company="RHEA System S.A.">
// Copyright (c) 2015-2020 RHEA System S.A.
// Copyright (c) 2015-2021 RHEA System S.A.
//
// Author: Sam Gerené, Merlin Bieze, Alex Vorobiev, Naron Phou, Alexander van Delft
//
Expand All @@ -25,8 +25,6 @@
namespace CDP4Common.Helpers
{
using System;
using System.Linq;
using System.Text.RegularExpressions;
using System.Collections.Generic;

using CDP4Common.Types;
Expand Down Expand Up @@ -64,152 +62,5 @@ public static ValueArray<string> CreateDefaultValueArray(int size)

return result;
}

/// <summary>
/// Regex used for conversion of HStore value to string
/// </summary>
private static readonly Regex HstoreToValueArrayRegex = new Regex(@"^\{(.*)\}$", RegexOptions.Singleline);

/// <summary>
/// Convert a string to a <see cref="ValueArray{T}"/>
/// </summary>
/// <typeparam name="T">The generic type of the <see cref="ValueArray{T}"/></typeparam>
/// <param name="valueArrayString">The string to convert</param>
/// <returns>The <see cref="ValueArray{T}"/></returns>
public static ValueArray<T> FromHstoreToValueArray<T>(string valueArrayString) =>
ToValueArray<T>(valueArrayString, HstoreToValueArrayRegex);

/// <summary>
/// Regex used for conversion of Json value to string
/// </summary>
private static readonly Regex JsonToValueArrayRegex = new Regex(@"^\[(.*)\]$", RegexOptions.Singleline);

/// <summary>
/// Convert a string to a <see cref="ValueArray{T}"/>
/// </summary>
/// <typeparam name="T">The generic type of the <see cref="ValueArray{T}"/></typeparam>
/// <param name="valueArrayString">The string to convert</param>
/// <returns>The <see cref="ValueArray{T}"/></returns>
public static ValueArray<T> FromJsonToValueArray<T>(string valueArrayString) =>
ToValueArray<T>(valueArrayString, JsonToValueArrayRegex);

/// <summary>
/// Convert a string to a <see cref="ValueArray{T}"/>
/// </summary>
/// <typeparam name="T">The generic type of the <see cref="ValueArray{T}"/></typeparam>
/// <param name="valueArrayString">The string to convert</param>
/// <param name="regex">The Regex use for conversion</param>
/// <returns>The <see cref="ValueArray{T}"/></returns>
private static ValueArray<T> ToValueArray<T>(string valueArrayString, Regex regex)
{
var arrayExtractResult = regex.Match(valueArrayString);
var extractedArrayString = arrayExtractResult.Groups[1].Value;

// match within 2 unescape double-quote the following content:
// 1) (no special char \ or ") 0..* times
// 2) (a pattern that starts with \ followed by any character (special included) and 0..* "non special" characters) 0..* times
var valueExtractionRegex = new Regex(@"""([^""\\]*(\\.[^""\\]*)*)""", RegexOptions.Singleline);
var test = valueExtractionRegex.Matches(extractedArrayString);

var stringValues = new List<string>();

foreach (Match match in test)
{
stringValues.Add(UnescapeString(match.Groups[1].Value));
}

var convertedStringList = stringValues.Select(m => (T)Convert.ChangeType(m, typeof(T))).ToList();

return new ValueArray<T>(convertedStringList);
}

/// <summary>
/// Convert a <see cref="ValueArray{String}"/> to the JSON format
/// </summary>
/// <param name="valueArray">The <see cref="ValueArray{String}"/></param>
/// <returns>The JSON string</returns>
public static string ToJsonString(ValueArray<string> valueArray)
{
var items = ValueArrayToStringList(valueArray);
return $"[{string.Join(",", items)}]";
}

/// <summary>
/// Convert a <see cref="ValueArray{String}"/> to the HStore format
/// </summary>
/// <param name="valueArray">The <see cref="ValueArray{String}"/></param>
/// <returns>The HStore string</returns>
public static string ToHstoreString(ValueArray<string> valueArray)
{
var items = ValueArrayToStringList(valueArray);
return $"{{{string.Join(";", items)}}}";
}

/// <summary>
/// Escape double quote and backslash
/// </summary>
/// <param name="valueArray"></param>
/// <returns>IEnumerable containing escaped strings</returns>
private static IEnumerable<string> ValueArrayToStringList(ValueArray<string> valueArray)
{
var items = valueArray.ToList();

for (var i = 0; i < items.Count; i++)
{
items[i] = $"\"{EscapeString(items[i])}\"";
}

return items;
}

/// <summary>
/// Contains a list of Keys and Values that can be used to replace each other when escaping and unescaping a <see cref="ValueArray{String}"/>
/// Details: Section 9 (String) of file: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
/// </summary>
private static readonly Dictionary<string, string> EscapePairs = new Dictionary<string, string>
{
{"\\\\", "\\"},
{"\\\"", "\""},
{"\\b", "\b"},
{"\\f", "\f"},
{"\\n", "\n"},
{"\\r", "\r"},
{"\\t", "\t"},
{"\\/", "/"}
};

/// <summary>
/// Escapes all characters that need to be treated as special characters in a <see cref="ValueArray{String}"/>
/// </summary>
/// <param name="unescapedString"></param>
/// <returns>The escaped string</returns>
public static string EscapeString(string unescapedString)
{
var escapedString = unescapedString;

foreach (var pair in EscapePairs)
{
escapedString = escapedString.Replace(pair.Value, pair.Key);
}

return escapedString;
}

/// <summary>
/// Unescapes all characters that need to be treated as special characters in a <see cref="ValueArray{String}"/>
/// </summary>
/// <param name="escapedString"></param>
/// <returns>The unescaped string</returns>
public static string UnescapeString(string escapedString)
{
var unescapeString = escapedString;

foreach (var pair in EscapePairs.Reverse())
{
unescapeString = unescapeString.Replace(pair.Key, pair.Value);
}

return unescapeString;
}
}
}
43 changes: 42 additions & 1 deletion CDP4JsonSerializer.NetCore.Tests/JsonSerializerTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,48 @@ public void VerifyThatSerializeGiveSameInput()
}
}
}


[Test]
public void VerifyThatValueSetDeserializationIsCorrectForStringThatRepresentEscapeCharacters()
{
var engineeringModel = new EngineeringModel(Guid.NewGuid(), this.cache, this.uri);
var iteration = new Iteration(Guid.NewGuid(), this.cache, this.uri);
engineeringModel.Iteration.Add(iteration);

var elementDefinition = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri);
iteration.Element.Add(elementDefinition);

var parameter = new Parameter(Guid.NewGuid(), this.cache, this.uri);
elementDefinition.Parameter.Add(parameter);

var parameterValueSet = new ParameterValueSet(Guid.Parse("049abaf8-d550-44b1-b32b-aa4b358f5d73"), this.cache, this.uri);
parameter.ValueSet.Add(parameterValueSet);

parameterValueSet.ValueSwitch = ParameterSwitchKind.MANUAL;
parameterValueSet.Manual = new ValueArray<string>(new[] { @"a\rb", @"a\tb", @"a\nb" });

var serializedParameterValueSet = this.serializer.SerializeToString(parameterValueSet, false);

IReadOnlyList<Dto.Thing> result;

using (var stream = StreamHelper.GenerateStreamFromString(serializedParameterValueSet))
{
result = this.serializer.Deserialize(stream).ToList();
}

using (var stream = new MemoryStream())
{
this.serializer.SerializeToStream(result, stream);
stream.Position = 0;

using (var reader = new StreamReader(stream))
{
var serializerResult = reader.ReadToEnd().Replace("\r", string.Empty).Replace("\n", string.Empty).Replace("\t", string.Empty).Replace(" ", string.Empty).Trim();
Assert.AreEqual(serializerResult.Length, serializedParameterValueSet.Length);
}
}
}

[Test]
[Category("Performance")]
public void PerformanceTest()
Expand Down
41 changes: 41 additions & 0 deletions CDP4JsonSerializer.Tests/JsonSerializerTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,47 @@ public void VerifyThatSerializeGiveSameInput()
}
}

[Test]
public void VerifyThatValueSetDeserializationIsCorrectForStringThatRepresentEscapeCharacters()
{
var engineeringModel = new EngineeringModel(Guid.NewGuid(), this.cache, this.uri);
var iteration = new Iteration(Guid.NewGuid(), this.cache, this.uri);
engineeringModel.Iteration.Add(iteration);

var elementDefinition = new ElementDefinition(Guid.NewGuid(), this.cache, this.uri);
iteration.Element.Add(elementDefinition);

var parameter = new Parameter(Guid.NewGuid(), this.cache, this.uri);
elementDefinition.Parameter.Add(parameter);

var parameterValueSet = new ParameterValueSet(Guid.Parse("049abaf8-d550-44b1-b32b-aa4b358f5d73"), this.cache, this.uri);
parameter.ValueSet.Add(parameterValueSet);

parameterValueSet.ValueSwitch = ParameterSwitchKind.MANUAL;
parameterValueSet.Manual = new ValueArray<string>(new[] { @"a\rb", @"a\tb", @"a\nb" });

var serializedParameterValueSet = this.serializer.SerializeToString(parameterValueSet, false);

IReadOnlyList<Dto.Thing> result;

using (var stream = StreamHelper.GenerateStreamFromString(serializedParameterValueSet))
{
result = this.serializer.Deserialize(stream).ToList();
}

using (var stream = new MemoryStream())
{
this.serializer.SerializeToStream(result, stream);
stream.Position = 0;

using (var reader = new StreamReader(stream))
{
var serializerResult = reader.ReadToEnd();
Assert.AreEqual(serializerResult.Length, serializedParameterValueSet.Length);
}
}
}

[Test]
[Category("Performance")]
public void PerformanceTest()
Expand Down
Loading

0 comments on commit 8960c60

Please sign in to comment.