Merge branch 'dev/AR-4_delinter_fixes' into dev/AR-1_serialization

This commit is contained in:
cankoluman
2014-05-21 14:23:26 +01:00
8 changed files with 148 additions and 197 deletions
@@ -1,20 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Reflection;
using Archetype.PropertyEditors;
using Archetype.Serialization;
using Moq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Editors;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
namespace Archetype.Tests.Serialization.Base
@@ -93,59 +85,11 @@ namespace Archetype.Tests.Serialization.Base
AssertAreEqual(model, resultfromArchetypeJson);
}
protected void CompoundModel_WithEscapedJson_Regression_Battery<T>(T model)
{
var propGuid = new Guid();
var dtd = new DataTypeDefinition(-1, String.Empty) {Id = 0};
var dataTypeService = new Mock<IDataTypeService>();
dataTypeService.Setup(dts => dts.GetDataTypeDefinitionById(Guid.Parse(propGuid.ToString()))).Returns(dtd);
dataTypeService.Setup(dts => dts.GetPreValuesCollectionByDataTypeId(dtd.Id)).Returns(new PreValueCollection(new Dictionary<string, PreValue>()));
var propValueEditor = new Mock<ArchetypePropertyEditor.ArchetypePropertyValueEditor>(new PropertyValueEditor());
propValueEditor.Setup(pe => pe.GetPropertyEditor(dtd)).Returns(new ArchetypePropertyEditor());
var archetype = ConvertModelToArchetype(model);
foreach (var prop in archetype.Fieldsets.SelectMany(fs => fs.Properties))
{
prop.DataTypeGuid = propGuid.ToString();
if (prop.Value.ToString().Contains("fieldsets"))
prop.PropertyEditorAlias = Constants.PropertyEditorAlias;
}
var archetypeJson = JsonConvert.SerializeObject(archetype);
Assert.IsNotNullOrEmpty(archetypeJson);
EnsureUmbracoContext(dataTypeService);
var propEditor = new ArchetypePropertyEditor.ArchetypePropertyValueEditor(new PropertyValueEditor());
var convertedJson = propEditor.ConvertEditorToDb(new ContentPropertyData(JObject.FromObject(archetype),
new PreValueCollection(new Dictionary<string, PreValue>()), new Dictionary<string, object>()),
archetypeJson);
Assert.IsNotNull(convertedJson);
var resultfromArchetypeJson = ConvertArchetypeJsonToModel<T>(archetypeJson);
var resultfromConvertedJson = ConvertArchetypeJsonToModel<T>(convertedJson.ToString().DelintArchetypeJson());
Assert.IsInstanceOf<T>(resultfromArchetypeJson);
AssertAreEqual(model, resultfromArchetypeJson);
Assert.IsInstanceOf<T>(resultfromConvertedJson);
AssertAreEqual(model, resultfromConvertedJson);
}
protected void NestedModel_Regression_Battery<T>(T model)
{
CompoundModel_Regression_Battery(model);
}
protected void NestedModel_WithEscapedJson_Regression_Battery<T>(T model)
{
CompoundModel_WithEscapedJson_Regression_Battery(model);
}
protected void ComplexModel_Regression_Battery<T>(T model)
{
CompoundModel_Regression_Battery(model);
@@ -49,7 +49,46 @@ namespace Archetype.Tests.Serialization.Delinter
_nestedClass.Nested.SlideShow = _referenceModel;
_nestedClass.Nested.Nested = GetNestedClass();
}
}
[Test]
public void Delinter_RemoveNewLine_ByPasses_Values()
{
const string _JSON_WITH_RESERVED_CHARS = @"{
""alias"": ""captions"",
""value"": ""{ \""fieldsets\"": [
{
\""properties\"":
[
{ \""alias\"": \""textstring\"" , \""value\"":\""test1 \r\n test1\r\n\""}], \""alias\"":\""textstringArray\"" },{\""properties\"": [ { \""alias\"":\""textstring\"",\""value\"":\""test2\r\ntest2\""}],\""alias\"":\""textstringArray\""},{\""properties\"":[{\""alias\"":\""textstring\"",\""value\"":\""test3b \""quote\"" \\r\\r\\rtest3\""}],\""alias\"":\""textstringArray\"" } ] }""
}";
var referenceModel = new Captions()
{
new TextString() {Text = @"test1
test1
"},
new TextString() {Text = @"test2
test2"},
new TextString() {Text = @"test3b ""quote"" \r\r\rtest3"}
};
var delintedJson = _JSON_WITH_RESERVED_CHARS.DelintArchetypeJson();
var actualModel = ConvertArchetypeJsonToModel<Captions>(delintedJson);
var t = JToken.Parse(delintedJson).ToString(Formatting.None);
Assert.IsInstanceOf<Captions>(actualModel);
Assert.IsNotNull(actualModel);
Assert.AreEqual(3, actualModel.Count);
foreach (var refItem in referenceModel)
{
var index = referenceModel.IndexOf(refItem);
AssertAreEqual(refItem, actualModel.ElementAt(index));
}
}
[Test]
public void ReferenceJsonAndModel_Equal_ExpectedJsonAndModel()
@@ -67,13 +67,6 @@ namespace Archetype.Tests.Serialization.Regression
CompoundModel_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModel_WithEscapedJson_Regression_Battery()
{
var compoundModel = _testHelper.GetModel<CompoundModel>();
CompoundModel_WithEscapedJson_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithMixedFieldsetVariant1_Regression_Battery()
{
@@ -81,13 +74,6 @@ namespace Archetype.Tests.Serialization.Regression
CompoundModel_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithMixedFieldsetVariant1_WithEscapedJson_Regression_Battery()
{
var compoundModel = _testHelper.GetModel<CompoundModelWithMixedFieldsetVariant1>();
CompoundModel_WithEscapedJson_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithMixedFieldsetVariant2_Regression_Battery()
{
@@ -95,13 +81,6 @@ namespace Archetype.Tests.Serialization.Regression
CompoundModel_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithMixedFieldsetVariant2_WithEscapedJson_Regression_Battery()
{
var compoundModel = _testHelper.GetModel<CompoundModelWithMixedFieldsetVariant2>();
CompoundModel_WithEscapedJson_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithFieldset_Regression_Battery()
{
@@ -109,13 +88,6 @@ namespace Archetype.Tests.Serialization.Regression
CompoundModel_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithFieldset_WithEscapedJson_Regression_Battery()
{
var compoundModel = _testHelper.GetModel<CompoundModelWithFieldset>();
CompoundModel_WithEscapedJson_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithList_Regression_Battery()
{
@@ -123,13 +95,6 @@ namespace Archetype.Tests.Serialization.Regression
CompoundModel_Regression_Battery(compoundModelWithList);
}
[Test]
public void CompoundModelWithList_WithEscapedJson_Regression_Battery()
{
var compoundModel = _testHelper.GetModel<CompoundModelWithList>();
CompoundModel_WithEscapedJson_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithMixedFieldsetWithList_Regression_Battery()
{
@@ -137,13 +102,6 @@ namespace Archetype.Tests.Serialization.Regression
CompoundModel_Regression_Battery(compoundModelWithList);
}
[Test]
public void CompoundModelWithMixedFieldsetWithList_WithEscapedJson_Regression_Battery()
{
var compoundModel = _testHelper.GetModel<CompoundModelWithMixedFieldsetWithList>();
CompoundModel_WithEscapedJson_Regression_Battery(compoundModel);
}
[Test]
public void CompoundModelWithFieldsetWithList_Regression_Battery()
{
@@ -151,13 +109,6 @@ namespace Archetype.Tests.Serialization.Regression
CompoundModel_Regression_Battery(compoundModelWithList);
}
[Test]
public void CompoundModelWithFieldsetWithList_WithEscapedJson_Regression_Battery()
{
var compoundModel = _testHelper.GetModel<CompoundModelWithFieldsetWithList>();
CompoundModel_WithEscapedJson_Regression_Battery(compoundModel);
}
[Test]
public void NestedModel_Regression_Battery()
{
@@ -165,13 +116,6 @@ namespace Archetype.Tests.Serialization.Regression
NestedModel_Regression_Battery(nestedModel);
}
[Test]
public void NestedModel_WithEscapedJson_Regression_Battery()
{
var nestedModel = _testHelper.GetModel<NestedModel>();
NestedModel_WithEscapedJson_Regression_Battery(nestedModel);
}
[Test]
public void NestedModelWithMixedFieldset_Regression_Battery()
{
@@ -179,13 +123,6 @@ namespace Archetype.Tests.Serialization.Regression
NestedModel_Regression_Battery(nestedModel);
}
[Test]
public void NestedModelWithMixedFieldset_WithEscapedJson_Regression_Battery()
{
var nestedModel = _testHelper.GetModel<NestedModelWithMixedFieldset>();
NestedModel_WithEscapedJson_Regression_Battery(nestedModel);
}
[Test]
public void NestedModelWithFieldset_Regression_Battery()
{
@@ -193,13 +130,6 @@ namespace Archetype.Tests.Serialization.Regression
NestedModel_Regression_Battery(nestedModel);
}
[Test]
public void NestedModelWithFieldset_WithEscapedJson_Regression_Battery()
{
var nestedModel = _testHelper.GetModel<NestedModelWithFieldset>();
NestedModel_WithEscapedJson_Regression_Battery(nestedModel);
}
[Test]
public void ComplexModel_Regression_Battery()
{
@@ -9,7 +9,7 @@
""properties"": [
{
""alias"": ""text"",
""value"": ""Test Text""
""value"": ""Test \r\nText \""quote\"" \r\n""
},
{
""alias"": ""integer"",
@@ -27,7 +27,9 @@ namespace Archetype.Tests.Serialization.Regression
DateTwo = null,
Id = 123,
NullableId = null,
Text = "Test Text"
Text = @"Test
Text ""quote""
"
};
}
@@ -126,8 +126,8 @@ namespace Archetype.PropertyEditors
{
var dtd = ApplicationContext.Current.Services.DataTypeService.GetDataTypeDefinitionById(Guid.Parse(propDef.DataTypeGuid));
var preValues = ApplicationContext.Current.Services.DataTypeService.GetPreValuesCollectionByDataTypeId(dtd.Id);
var propData = new ContentPropertyData(propDef.Value, preValues, new Dictionary<string, object>());
var propEditor = GetPropertyEditor(dtd);
var propData = new ContentPropertyData(propDef.Value, preValues, new Dictionary<string, object>());
var propEditor = PropertyEditorResolver.Current.GetByAlias(dtd.PropertyEditorAlias);
propDef.Value = propEditor.ValueEditor.ConvertEditorToDb(propData, propDef.Value);
}
catch (Exception ex)
@@ -38,22 +38,9 @@ namespace Archetype.Serialization
var jToken = JToken.ReadFrom(reader);
if (jToken == null)
return null;
var obj = Activator.CreateInstance(objectType);
return null;
if (null != obj as IEnumerable<object>)
{
JToken enumerableToken;
if (TryParseJTokenAsEnumerable(jToken, out enumerableToken)
|| TryParseJTokenAsEnumerable(jToken["value"], out enumerableToken))
return DeserializeEnumerableObject(obj, enumerableToken);
}
return null == jToken as JArray
? DeserializeObject(obj, jToken)
: PopulateProperties(obj, jToken);
return DeserializeJson(objectType, GetDelintedJToken(jToken));
}
public override bool CanConvert(Type objectType)
@@ -63,17 +50,48 @@ namespace Archetype.Serialization
#endregion
#region private methods - deserialization
#region private methods - deserialization
private JToken GetDelintedJToken(JToken jToken)
{
return JToken.Parse(jToken.ToString(Formatting.None).DelintArchetypeJson());
}
private object DeserializeJson(Type objectType, JToken jToken)
{
var obj = Activator.CreateInstance(objectType);
if (null != obj as IEnumerable<object>)
{
JToken enumerableToken;
if (TryParseJTokenAsEnumerable(jToken, out enumerableToken)
|| (jToken.SelectToken("value") != null && TryParseJTokenAsEnumerable(jToken["value"], out enumerableToken)))
return DeserializeEnumerableObject(obj, enumerableToken);
}
return null == jToken as JArray
? DeserializeObject(obj, jToken)
: PopulateProperties(obj, jToken);
}
private object DeserializeEnumerableObject(object obj, JToken jToken)
{
var model = obj as IEnumerable<object>;
var itemType = model.GetType().BaseType.GetGenericArguments().First();
foreach (var fs in jToken["fieldsets"].Where(fs => fs["alias"].ToString().Equals(GetFieldsetName(itemType))))
{
var item = JsonConvert.DeserializeObject(
fs["properties"].ToString().DelintArchetypeJson(), itemType, this);
var model = obj as IEnumerable<object>;
var fsToken = jToken;
var itemType = model.GetType().GetGenericArguments().FirstOrDefault();
if (itemType == null)
{
itemType = model.GetType().BaseType.GetGenericArguments().First();
fsToken = fsToken["fieldsets"];
}
foreach (var fs in fsToken.Where(fs => fs["alias"].ToString().Equals(GetFieldsetName(itemType))))
{
var item = DeserializeJson(itemType, fs["properties"]);
obj.GetType().GetMethod("Add").Invoke(obj, new[] { item });
}
@@ -208,15 +226,12 @@ namespace Archetype.Serialization
private object GetPropertyValue(PropertyInfo propertyInfo, JToken propJToken)
{
return IsTypeArchetypeDatatype(propertyInfo.PropertyType)
? JsonConvert.DeserializeObject(propJToken.ToString().DelintArchetypeJson(), propertyInfo.PropertyType,
this)
? DeserializeJson(propertyInfo.PropertyType, propJToken)
: IsTypeIEnumerableArchetypeDatatype(propertyInfo.PropertyType)
? JsonConvert.DeserializeObject(propJToken["value"].SelectToken("fieldsets").ToString(), propertyInfo.PropertyType,
this)
? DeserializeJson(propertyInfo.PropertyType, propJToken["value"].SelectToken("fieldsets"))
: GetDeserializedPropertyValue(propJToken, propertyInfo.PropertyType);
}
private JToken ParseJTokenFromItem(JToken jToken, string itemAlias)
{
return (jToken.SelectToken("alias") != null && jToken["alias"].ToString().Equals(itemAlias))
@@ -266,8 +281,14 @@ namespace Archetype.Serialization
resultToken = null;
//To Do: Strange newtonsoft behaviour
var fsToken = jToken.Parent == null ? jToken["fieldsets"] : jToken.Parent.Children().First();
var jTokenEnumerable = jToken != null && fsToken != null && fsToken.Any();
var fsToken = jToken.Parent != null
? jToken.Parent.Children().First() is JArray
? jToken.Parent.Children().First()
: jToken.Parent.Children().First()["fieldsets"]
: jToken.SelectToken("fieldsets");
var jTokenEnumerable = jToken != null
&& fsToken != null && fsToken is JArray;
if (jTokenEnumerable)
resultToken = jToken;
@@ -1,24 +1,24 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Archetype.Serialization
{
public enum DelinterStep
{
{
RemoveNewLine = 0,
LabelFieldsets,
LabelProperties,
LabelAlias,
LabelValue,
RemoveWhiteSpace,
UnescapeLabels,
UnescapeAlias,
UnescapeValues,
FixNestedFieldsets,
RemoveWhiteSpace
FixNestedFieldsets
}
public enum DelinterAction
{
Remove = 0,
RemoveWhiteSpace,
UnescapeLabels,
UnescapeValues,
FixNestedFieldsets
@@ -26,9 +26,9 @@ namespace Archetype.Serialization
public class ArchetypeJsonDelinter
{
public IDictionary<DelinterStep, DelinterAction> Pipeline { get; set; }
public IDictionary<DelinterStep, Regex> Tokens { get; set; }
public IDictionary<DelinterAction, Func<Match,string>> Actions { get; set; }
public IDictionary<DelinterStep, DelinterAction> Pipeline { get; private set; }
public IDictionary<DelinterStep, Regex> Tokens { get; private set; }
public IDictionary<DelinterAction, Func<string, Regex, string>> Actions { get; private set; }
public ArchetypeJsonDelinter()
{
@@ -42,11 +42,7 @@ namespace Archetype.Serialization
foreach (var step in Pipeline.Keys)
{
var action = Pipeline[step];
while (Tokens[step].IsMatch(buffer))
{
buffer = Tokens[step]
.Replace(buffer, match => Actions[action](match));
}
buffer = Actions[action](buffer, Tokens[step]);
}
return buffer;
@@ -56,35 +52,54 @@ namespace Archetype.Serialization
{
Tokens = new Dictionary<DelinterStep, Regex>
{
{DelinterStep.RemoveNewLine, new Regex(@"\\r|\\n|[\r\n]", RegexOptions.Multiline)},
{DelinterStep.LabelFieldsets, new Regex(@"\s*\\+""(fieldsets)\\+"":\s*")},
{DelinterStep.LabelProperties, new Regex(@"\s*\\+""(properties)\\+"":\s*")},
{DelinterStep.LabelAlias, new Regex(@"\s*\\+""(alias)\\+"":\s*")},
{DelinterStep.LabelValue, new Regex(@"\s*\\+""(value)\\+"":\s*")},
{DelinterStep.UnescapeValues, new Regex(@"""(alias|value)"":\\+""(.*?)\\+""")},
{DelinterStep.FixNestedFieldsets, new Regex(@"""(value)"":\s*?""({""fieldsets"".+?})""")},
{DelinterStep.RemoveWhiteSpace, new Regex(@"\s+(?=([^""]*""[^""]*"")*[^""]*$)")}
{DelinterStep.RemoveNewLine, new Regex(@"(?<=\,){0,1}(\\+r\\+n)(?=\\*?""(alias|value|properties|fieldsets)\\*?"":\{*?(\\*|""|\[))|(\r|\n)+|(\\+r\\+n)(?=\s*?(\{|\}|\]))|(\\+r\\n+)(?=\s+\\*?""(alias|value|properties|fieldsets))")},
{DelinterStep.RemoveWhiteSpace, new Regex(@"(""\S+?"":)(\s+?)("")|([\{\[\}\],]|(?<!\\)"")(\s+)([\{\[\}\],]|(?<!\\)"")")},
{DelinterStep.UnescapeLabels, new Regex(@"\\+""(fieldsets|properties|alias|value)\\+"":(\s*)")},
{DelinterStep.UnescapeAlias, new Regex(@"""(alias)"":\\+""(.*?)\\+""")},
{DelinterStep.UnescapeValues, new Regex(@"""(value)"":\\+""(.*?)\\+""(?=\s*?\})")},
{DelinterStep.FixNestedFieldsets, new Regex(@"""(value)"":\s*?""\{\s*?(""fieldsets""[\S\s]+?)}""")}
};
Actions = new Dictionary<DelinterAction, Func<Match, string>>
Actions = new Dictionary<DelinterAction, Func<string, Regex, string>>
{
{DelinterAction.Remove, match => String.Empty},
{DelinterAction.UnescapeLabels, match => String.Format(@"""{0}"":", match.Groups[1])},
{DelinterAction.UnescapeValues, match => String.Format(@"""{0}"":""{1}""", match.Groups[1], match.Groups[2])},
{DelinterAction.FixNestedFieldsets, match => String.Format(@"""{0}"":{1}", match.Groups[1], match.Groups[2])}
{DelinterAction.Remove, (input, pattern) =>
RecursiveReplace(input, pattern, match => String.Empty)
},
{DelinterAction.RemoveWhiteSpace, (input, pattern) =>
RecursiveReplace(input.Trim(), pattern, match =>
String.Format("{0}{1}{2}{3}", match.Groups[1].Value,
match.Groups[3].Value, match.Groups[4].Value, match.Groups[6].Value))
},
{DelinterAction.UnescapeLabels, (input, pattern) =>
RecursiveReplace(input, pattern, match => String.Format(@"""{0}"":", match.Groups[1].Value))
},
{DelinterAction.UnescapeValues, (input, pattern) =>
RecursiveReplace(input, pattern, match => String.Format(@"""{0}"":""{1}""", match.Groups[1].Value, match.Groups[2].Value))
},
{DelinterAction.FixNestedFieldsets, (input, pattern) =>
RecursiveReplace(input, pattern, match => String.Format(@"""{0}"":{{{1}}}", match.Groups[1].Value, match.Groups[2].Value))
}
};
Pipeline = new Dictionary<DelinterStep, DelinterAction>
{
{DelinterStep.RemoveNewLine, DelinterAction.Remove},
{DelinterStep.LabelFieldsets, DelinterAction.UnescapeLabels},
{DelinterStep.LabelProperties, DelinterAction.UnescapeLabels},
{DelinterStep.LabelAlias, DelinterAction.UnescapeLabels},
{DelinterStep.LabelValue, DelinterAction.UnescapeLabels},
{DelinterStep.RemoveNewLine, DelinterAction.Remove},
{DelinterStep.UnescapeLabels, DelinterAction.UnescapeLabels},
{DelinterStep.UnescapeAlias, DelinterAction.UnescapeValues},
{DelinterStep.UnescapeValues, DelinterAction.UnescapeValues},
{DelinterStep.FixNestedFieldsets, DelinterAction.FixNestedFieldsets},
{DelinterStep.RemoveWhiteSpace, DelinterAction.Remove}
{DelinterStep.RemoveWhiteSpace, DelinterAction.RemoveWhiteSpace},
};
}
private string RecursiveReplace(string input, Regex pattern, Func<Match, string> matchFunc)
{
var buffer = input;
while (pattern.IsMatch(buffer))
{
buffer = pattern.Replace(buffer, match => matchFunc(match));
}
return buffer;
}
}
}