Fix various bugs, improve docs, allow 'null' in JSON, specify behaviour of must_match better
This commit is contained in:
parent
8e3383baec
commit
e2cd6caa70
12 changed files with 172 additions and 102 deletions
26
AspectedRouting.Test/MappingTest.cs
Normal file
26
AspectedRouting.Test/MappingTest.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using Xunit;
|
||||
|
||||
namespace AspectedRouting.Test
|
||||
{
|
||||
public class MappingTest
|
||||
{
|
||||
[Fact]
|
||||
public static void SimpleMapping_SimpleHighway_GivesResult()
|
||||
{
|
||||
var maxspeed = new Mapping(new[] {"residential", "living_street"},
|
||||
new[] {
|
||||
new Constant(30),
|
||||
new Constant(20)
|
||||
}
|
||||
);
|
||||
var resMaxspeed= maxspeed.Evaluate(new Context(), new Constant("residential"));
|
||||
Assert.Equal(30, resMaxspeed);
|
||||
var livingStreetMaxspeed= maxspeed.Evaluate(new Context(), new Constant("living_street"));
|
||||
Assert.Equal(20, livingStreetMaxspeed);
|
||||
var undefinedSpeed = maxspeed.Evaluate(new Context(), new Constant("some_unknown_highway_type"));
|
||||
Assert.Null(undefinedSpeed);
|
||||
}
|
||||
}
|
||||
}
|
48
AspectedRouting.Test/MustMatchTest.cs
Normal file
48
AspectedRouting.Test/MustMatchTest.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System.Collections.Generic;
|
||||
using AspectedRouting.Language;
|
||||
using AspectedRouting.Language.Functions;
|
||||
using Xunit;
|
||||
|
||||
namespace AspectedRouting.Test
|
||||
{
|
||||
public class MustMatchTest
|
||||
{
|
||||
[Fact]
|
||||
public void MustMatch_SimpleInput()
|
||||
{
|
||||
var mapValue = new Mapping(new[] {"residential", "living_street"},
|
||||
new[] {
|
||||
new Constant("yes"),
|
||||
new Constant("no")
|
||||
});
|
||||
var mapTag = new Mapping(new[] {"highway"}, new[] {mapValue});
|
||||
var mm = Funcs.MustMatch
|
||||
.Apply(
|
||||
new Constant(new[] {new Constant("highway")}),
|
||||
Funcs.StringStringToTags.Apply(mapTag)
|
||||
)
|
||||
;
|
||||
|
||||
|
||||
var residential = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||
{"highway", "residential"}
|
||||
})).Evaluate(new Context());
|
||||
Assert.Equal("yes", residential);
|
||||
|
||||
var living = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||
{"highway", "living_street"}
|
||||
})).Evaluate(new Context());
|
||||
Assert.Equal("no", living);
|
||||
|
||||
var unknown = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||
{"highway", "unknown_type"}
|
||||
})).Evaluate(new Context());
|
||||
Assert.Equal("yes", unknown);
|
||||
|
||||
var missing = mm.Apply(new Constant(new Dictionary<string, string> {
|
||||
{"proposed:highway", "unknown_type"}
|
||||
})).Evaluate(new Context());
|
||||
Assert.Equal("no", missing);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,10 +12,10 @@
|
|||
</TestAncestor>
|
||||
</SessionState></s:String>
|
||||
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=a6a74f48_002D8456_002D43c7_002Dbbee_002Dd3da33a8a4be/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" Name="Integration_TestExamples" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=a6a74f48_002D8456_002D43c7_002Dbbee_002Dd3da33a8a4be/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Integration_TestExamples" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="/home/pietervdvn/git/AspectedRouting/AspectedRouting.Test" Presentation="&lt;AspectedRouting.Test&gt;" />
|
||||
</SessionState></s:String>
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d2e3d58f_002Debff_002D4fb5_002D8d18_002Deafe85f4773d/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="SpecializeToCommonTypes_ValueAndFuncType_ShouldFail" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d2e3d58f_002Debff_002D4fb5_002D8d18_002Deafe85f4773d/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" Name="SpecializeToCommonTypes_ValueAndFuncType_ShouldFail" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||
<Project Location="/home/pietervdvn/git/AspectedRouting/AspectedRouting.Test" Presentation="&lt;AspectedRouting.Test&gt;" />
|
||||
</SessionState></s:String>
|
||||
</wpf:ResourceDictionary>
|
|
@ -26,8 +26,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
, UnApply(
|
||||
IsFunc(Funcs.StringStringToTags),
|
||||
Assign(collectedMapping))
|
||||
).Invoke(bare))
|
||||
{
|
||||
).Invoke(bare)) {
|
||||
AddDep(Funcs.FirstOf.Name);
|
||||
return "first_match_of(tags, result, \n" +
|
||||
" " + ToLua(order.First(), key) + "," +
|
||||
|
@ -42,8 +41,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
, UnApply(
|
||||
IsFunc(Funcs.StringStringToTags),
|
||||
Assign(collectedMapping))
|
||||
).Invoke(bare))
|
||||
{
|
||||
).Invoke(bare)) {
|
||||
AddDep(Funcs.MustMatch.Name);
|
||||
return "must_match(tags, result, \n" +
|
||||
" " + ToLua(order.First(), key) + "," +
|
||||
|
@ -54,37 +52,32 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
if (UnApply(
|
||||
IsFunc(Funcs.MemberOf),
|
||||
Any
|
||||
).Invoke(bare))
|
||||
{
|
||||
).Invoke(bare)) {
|
||||
AddDep("memberOf");
|
||||
return "memberOf(funcName, parameters, tags, result)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
var collectedList = new List<IExpression>();
|
||||
var func = new List<IExpression>();
|
||||
if (
|
||||
UnApply(
|
||||
UnApply(IsFunc(Funcs.Dot), Assign(func)),
|
||||
UnApply(IsFunc(Funcs.ListDot),
|
||||
Assign(collectedList))).Invoke(bare))
|
||||
{
|
||||
Assign(collectedList))).Invoke(bare)) {
|
||||
var exprs = (IEnumerable<IExpression>) ((Constant) collectedList.First()).Evaluate(_context);
|
||||
var luaExprs = new List<string>();
|
||||
var funcName = func.First().ToString().TrimStart('$');
|
||||
AddDep(funcName);
|
||||
foreach (var expr in exprs)
|
||||
{
|
||||
foreach (var expr in exprs) {
|
||||
var c = new List<IExpression>();
|
||||
if (UnApply(IsFunc(Funcs.Const), Assign(c)).Invoke(expr))
|
||||
{
|
||||
if (UnApply(IsFunc(Funcs.Const), Assign(c)).Invoke(expr)) {
|
||||
luaExprs.Add(ToLua(c.First(), key));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (expr.Types.First() is Curry curry
|
||||
&& curry.ArgType.Equals(Typs.Tags))
|
||||
{
|
||||
&& curry.ArgType.Equals(Typs.Tags)) {
|
||||
var lua = ToLua(expr, key);
|
||||
luaExprs.Add(lua);
|
||||
}
|
||||
|
@ -93,6 +86,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
return "\n " + funcName + "({\n " + string.Join(",\n ", luaExprs) +
|
||||
"\n })";
|
||||
}
|
||||
|
||||
collectedMapping.Clear();
|
||||
var dottedFunction = new List<IExpression>();
|
||||
dottedFunction.Clear();
|
||||
|
@ -104,8 +98,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
UnApply(
|
||||
IsFunc(Funcs.StringStringToTags),
|
||||
Assign(collectedMapping))).Invoke(bare)
|
||||
)
|
||||
{
|
||||
) {
|
||||
var mapping = (Mapping) collectedMapping.First();
|
||||
var baseFunc = (Function) dottedFunction.First();
|
||||
AddDep(baseFunc.Name);
|
||||
|
@ -121,8 +114,7 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
// The expression might be a function which still expects a string (the value from the tag) as argument
|
||||
if (!(bare is Mapping) &&
|
||||
bare.Types.First() is Curry curr &&
|
||||
curr.ArgType.Equals(Typs.String))
|
||||
{
|
||||
curr.ArgType.Equals(Typs.String)) {
|
||||
var applied = new Apply(bare, new Constant(curr.ArgType, ("tags", "\"" + key + "\"")));
|
||||
return ToLua(applied.Optimize(), key);
|
||||
}
|
||||
|
@ -130,37 +122,31 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
// The expression might consist of multiple nested functions
|
||||
var fArgs = bare.DeconstructApply();
|
||||
if (fArgs != null)
|
||||
{
|
||||
if (fArgs != null) {
|
||||
var (f, args) = fArgs.Value;
|
||||
var baseFunc = (Function) f;
|
||||
|
||||
if (baseFunc.Name.Equals(Funcs.Id.Name))
|
||||
{
|
||||
if (baseFunc.Name.Equals(Funcs.Id.Name)) {
|
||||
// This is an ugly hack
|
||||
return ToLua(args.First());
|
||||
}
|
||||
|
||||
if(baseFunc.Name.Equals(Funcs.Dot.Name))
|
||||
{
|
||||
|
||||
if (args.Count == 1 || forceFirstArgInDot)
|
||||
{
|
||||
if (baseFunc.Name.Equals(Funcs.Dot.Name)) {
|
||||
if (args.Count == 1 || forceFirstArgInDot) {
|
||||
return ToLua(args[0]);
|
||||
}
|
||||
|
||||
|
||||
var argsAsLua = args.Select(arg => ToLua(arg, key)).ToList();
|
||||
var fName = argsAsLua[0];
|
||||
var actualArgs =
|
||||
string.Join(",",argsAsLua.GetRange(1, argsAsLua.Count - 1));
|
||||
return $"{fName}({actualArgs})";
|
||||
var actualArgs =
|
||||
string.Join(",", argsAsLua.GetRange(1, argsAsLua.Count - 1));
|
||||
return $"{fName}({actualArgs})";
|
||||
}
|
||||
|
||||
|
||||
AddDep(baseFunc.Name);
|
||||
|
||||
var argExpressions = new List<string>();
|
||||
foreach (var arg in args)
|
||||
{
|
||||
foreach (var arg in args) {
|
||||
argExpressions.Add(ToLua(arg, key));
|
||||
}
|
||||
|
||||
|
@ -169,14 +155,12 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
|
||||
|
||||
var collected = new List<IExpression>();
|
||||
switch (bare)
|
||||
{
|
||||
switch (bare) {
|
||||
case LuaLiteral lua:
|
||||
return lua.Lua;
|
||||
case FunctionCall fc:
|
||||
var called = _context.DefinedFunctions[fc.CalledFunctionName];
|
||||
if (called.ProfileInternal)
|
||||
{
|
||||
if (called.ProfileInternal) {
|
||||
return called.Name;
|
||||
}
|
||||
|
||||
|
@ -189,15 +173,12 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
return MappingToLua(m).Indent();
|
||||
case Function f:
|
||||
var fName = f.Name.TrimStart('$');
|
||||
if (Funcs.Builtins.ContainsKey(fName))
|
||||
{
|
||||
if (Funcs.Builtins.ContainsKey(fName)) {
|
||||
AddDep(f.Name);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
var definedFunc = _context.DefinedFunctions[fName];
|
||||
if (definedFunc.ProfileInternal)
|
||||
{
|
||||
if (definedFunc.ProfileInternal) {
|
||||
return f.Name;
|
||||
}
|
||||
|
||||
|
@ -219,13 +200,11 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
public string MappingToLua(Mapping m)
|
||||
{
|
||||
var isConstant = true;
|
||||
var contents = m.StringToResultFunctions.Select(kv =>
|
||||
{
|
||||
var contents = m.StringToResultFunctions.Select(kv => {
|
||||
var (key, expr) = kv;
|
||||
var left = "[\"" + key + "\"]";
|
||||
|
||||
if (Regex.IsMatch(key, "^[a-zA-Z][_a-zA-Z-9]*$"))
|
||||
{
|
||||
if (Regex.IsMatch(key, "^[a-zA-Z][_a-zA-Z-9]*$")) {
|
||||
left = key;
|
||||
}
|
||||
|
||||
|
@ -233,7 +212,8 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
if (luaExpr.Contains("tags")) {
|
||||
isConstant = false;
|
||||
}
|
||||
return left + " = " + luaExpr ;
|
||||
|
||||
return left + " = " + luaExpr;
|
||||
}
|
||||
);
|
||||
var mapping =
|
||||
|
@ -245,19 +225,17 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
}
|
||||
|
||||
return mapping;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Neatly creates a value expression in lua, based on a constant
|
||||
/// Neatly creates a value expression in lua, based on a constant
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns></returns>
|
||||
private string ConstantToLua(Constant c)
|
||||
{
|
||||
var o = c.Evaluate(_context);
|
||||
switch (o)
|
||||
{
|
||||
switch (o) {
|
||||
case LuaLiteral lua:
|
||||
return lua.Lua;
|
||||
case IExpression e:
|
||||
|
@ -268,15 +246,15 @@ namespace AspectedRouting.IO.LuaSkeleton
|
|||
return "" + d;
|
||||
case string s:
|
||||
return '"' + s.Replace("\"", "\\\"") + '"';
|
||||
case null:
|
||||
return "nil";
|
||||
case ValueTuple<string, string> unpack:
|
||||
return unpack.Item1 + "[" + unpack.Item2 + "]";
|
||||
case IEnumerable<object> ls:
|
||||
var t = ((ListType) c.Types.First()).InnerType;
|
||||
return "{" + string.Join(", ", ls.Select(obj =>
|
||||
{
|
||||
return "{" + string.Join(", ", ls.Select(obj => {
|
||||
var objInConstant = new Constant(t, obj);
|
||||
if (obj is Constant asConstant)
|
||||
{
|
||||
if (obj is Constant asConstant) {
|
||||
objInConstant = asConstant;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace AspectedRouting.IO.itinero1
|
|||
{
|
||||
public static class LuaStringExtensions
|
||||
{
|
||||
|
||||
public static string ToLuaTable(this Dictionary<string, string> tags)
|
||||
{
|
||||
var contents = tags.Select(kv =>
|
||||
|
|
|
@ -301,6 +301,9 @@ namespace AspectedRouting.IO.jsonParser
|
|||
return new Constant(s);
|
||||
}
|
||||
|
||||
if (e.ValueKind == JsonValueKind.Null) {
|
||||
return new Constant(new Var("a"), null);
|
||||
}
|
||||
|
||||
throw new Exception("Could not parse " + e);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
function if_then_else(condition, thn, els)
|
||||
if (condition ~= nil and (condition == "yes" or condition == true or condition == "true") then
|
||||
if (condition ~= nil and (condition == "yes" or condition == true or condition == "true")) then
|
||||
return thn
|
||||
else
|
||||
return els -- if no third parameter is given, 'els' will be nil
|
||||
|
|
|
@ -15,18 +15,26 @@ When applied on the tags {"a" : "X"}, this yields the table {"a":"yes", "b":"yes
|
|||
MustMatch checks that every key in this last table yields yes - even if it is not in the original tags!
|
||||
|
||||
|
||||
Arguments:
|
||||
- The tags of the feature
|
||||
- The result table, where 'attributes_to_keep' might be set
|
||||
- needed_keys which indicate which keys must be present in 'tags'
|
||||
- table which is the table to match
|
||||
|
||||
]]
|
||||
function must_match(tags, result, needed_keys, table)
|
||||
for _, key in ipairs(needed_keys) do
|
||||
local v = table[key] -- use the table here, as a tag that must _not_ match might be 'nil' in the tags
|
||||
local v = tags[key]
|
||||
if (v == nil) then
|
||||
-- a key is missing...
|
||||
return false
|
||||
end
|
||||
|
||||
local mapping = table[key]
|
||||
if (type(mapping) == "table") then
|
||||
-- we have to map the value with a function:
|
||||
local resultValue = mapping[v]
|
||||
if (resultValue == nil or
|
||||
if (resultValue ~= nil or -- actually, having nil for a mapping is fine for this function!.
|
||||
resultValue == false or
|
||||
resultValue == "no" or
|
||||
resultValue == "false") then
|
||||
|
|
|
@ -7,6 +7,13 @@ using Type = AspectedRouting.Language.Typ.Type;
|
|||
|
||||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
/// <summary>
|
||||
/// The constructor takes a dictionary "key --> expression".
|
||||
/// If a string is given as argument, the respective argument is returned. If the key is not found, 'null' is returned
|
||||
///
|
||||
/// If a table/dictionary/collection of tags is given, the key of the arguments is used and the expression is used as function on every respective value of the argument.
|
||||
/// If the key is not found, null is returned for this expression instead.
|
||||
/// </summary>
|
||||
public class Mapping : Function
|
||||
{
|
||||
public readonly Dictionary<string, IExpression> StringToResultFunctions;
|
||||
|
|
|
@ -2,47 +2,44 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using AspectedRouting.Language.Expression;
|
||||
using AspectedRouting.Language.Typ;
|
||||
using Type = AspectedRouting.Language.Typ.Type;
|
||||
|
||||
namespace AspectedRouting.Language.Functions
|
||||
{
|
||||
public class MustMatch : Function
|
||||
{
|
||||
public override string Description { get; } =
|
||||
"Checks that every specified key is present and gives a non-false value\n." +
|
||||
"" +
|
||||
"\n" +
|
||||
"If, on top, a value is present with a mapping, every key/value will be executed and must return a value that is not 'no' or 'false'\n" +
|
||||
"Note that this is a privileged builtin function, as the parser will automatically inject the keys used in the called function.";
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string>
|
||||
{
|
||||
"neededKeys (filled in by parser)",
|
||||
"f"
|
||||
};
|
||||
|
||||
public MustMatch() : base("mustMatch", true,
|
||||
new[]
|
||||
{
|
||||
new[] {
|
||||
// [String] -> (Tags -> [string]) -> Tags -> bool
|
||||
Curry.ConstructFrom(Typs.Bool, // Result type on top!
|
||||
new ListType(Typs.String), // List of keys to check for
|
||||
new Curry(Typs.Tags, new ListType(Typs.String)), // The function to execute on every key
|
||||
Typs.Tags // The tags to apply this on
|
||||
)
|
||||
})
|
||||
{
|
||||
}
|
||||
}) { }
|
||||
|
||||
private MustMatch(IEnumerable<Type> types) : base("mustMatch", types)
|
||||
{
|
||||
}
|
||||
private MustMatch(IEnumerable<Type> types) : base("mustMatch", types) { }
|
||||
|
||||
public override string Description { get; } = Utils.Lines(
|
||||
"Checks that every specified key is present and gives a non-false value.\n",
|
||||
"If, on top, a value is present with a mapping, every key/value will be executed and must return a value that is not 'no' or 'false'. Note that 'null' is considered as true here too!",
|
||||
"Note that this is a privileged builtin function, as the parser will automatically inject the keys used in the called function.\n",
|
||||
"",
|
||||
"Usage example",
|
||||
"-------------",
|
||||
"",
|
||||
"`\'mustMatchKeys\': { \"highway\": { \"proposed\": \"no\", \"undefined\":null }}`",
|
||||
"which will return 'yes' for {highway=residential}, {highway=living_street}, ..., but return 'no' for {highway=proposed}, but also for {some_other_key=xxx}",
|
||||
"Also note that {highway=undefined} will return (somewhat surprisingly) 'yes' too - as null-values are considered as true here too!");
|
||||
|
||||
public override List<string> ArgNames { get; } = new List<string> {
|
||||
"neededKeys (do not specify in source code -added automatically by the parser)",
|
||||
"f"
|
||||
};
|
||||
|
||||
public override IExpression Specialize(IEnumerable<Type> allowedTypes)
|
||||
{
|
||||
var unified = Types.SpecializeTo(allowedTypes);
|
||||
if (unified == null)
|
||||
{
|
||||
if (unified == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -51,36 +48,38 @@ namespace AspectedRouting.Language.Functions
|
|||
|
||||
public override object Evaluate(Context c, params IExpression[] arguments)
|
||||
{
|
||||
var neededKeys = (IEnumerable<object>) arguments[0].Evaluate(c);
|
||||
var neededKeys = (IEnumerable<object>) arguments[0].Evaluate(c);
|
||||
var function = arguments[1];
|
||||
|
||||
var tags = (Dictionary<string, string>) arguments[2].Evaluate(c);
|
||||
|
||||
foreach (var oo in neededKeys)
|
||||
{
|
||||
foreach (var oo in neededKeys) {
|
||||
var o = oo;
|
||||
while (o is IExpression e)
|
||||
{
|
||||
while (o is IExpression e) {
|
||||
o = e.Evaluate(c);
|
||||
}
|
||||
if (!(o is string tagKey))
|
||||
{
|
||||
|
||||
if (!(o is string tagKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tags.ContainsKey(tagKey)) return "no";
|
||||
if (!tags.ContainsKey(tagKey)) {
|
||||
// A required key is missing: return 'no'
|
||||
return "no";
|
||||
}
|
||||
}
|
||||
|
||||
var result = (IEnumerable<object>) function.Evaluate(c, new Constant(tags));
|
||||
|
||||
if (!result.Any(o =>
|
||||
if (result.Any(o =>
|
||||
o == null ||
|
||||
(o is string s && (s.Equals("no") || s.Equals("false")))))
|
||||
{
|
||||
return "yes";
|
||||
o is string s && (s.Equals("no") || s.Equals("false")))) {
|
||||
// The mapped function is executed. If the mapped function gives 'no', null or 'false' for any value, "no" is returned
|
||||
return "no";
|
||||
}
|
||||
|
||||
return "no";
|
||||
return "yes";
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -43,7 +43,7 @@ namespace AspectedRouting.Tests
|
|||
var tags = new Dictionary<string, string>();
|
||||
for (var i = 0; i < keys.Count; i++) {
|
||||
if (i < vals.Count && !string.IsNullOrEmpty(vals[i])) {
|
||||
tags[keys[i]] = vals[i];
|
||||
tags[keys[i]] = vals[i].Trim(new []{'"'}).Replace("\"","\\\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ namespace AspectedRouting.Tests
|
|||
{
|
||||
if (i < vals.Count && !string.IsNullOrEmpty(vals[i]))
|
||||
{
|
||||
tags[keys[i]] = vals[i];
|
||||
tags[keys[i]] = vals[i].Trim(new []{'\"'}).Replace("\"","\\\"");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue