diff --git a/src/Pather.CSharp/IResolver.cs b/src/Pather.CSharp/IResolver.cs index c3b2a4d..a7ca6ae 100644 --- a/src/Pather.CSharp/IResolver.cs +++ b/src/Pather.CSharp/IResolver.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq.Expressions; using Pather.CSharp.PathElements; namespace Pather.CSharp @@ -41,5 +43,14 @@ public interface IResolver /// /// object ResolveSafe(object target, string path); + + /// + /// extract field path by a lambda expression
+ /// usage example: ExtractPath(()=>student.subjects[0].score) returns "student.subjects[0].score" + ///
+ /// + /// + /// + string ExtractPath(Expression> lambdaExpression); } } \ No newline at end of file diff --git a/src/Pather.CSharp/Pather.CSharp.csproj b/src/Pather.CSharp/Pather.CSharp.csproj index e57c47c..babf220 100644 --- a/src/Pather.CSharp/Pather.CSharp.csproj +++ b/src/Pather.CSharp/Pather.CSharp.csproj @@ -21,6 +21,7 @@ + diff --git a/src/Pather.CSharp/Resolver.cs b/src/Pather.CSharp/Resolver.cs index 6251b60..cf0483d 100644 --- a/src/Pather.CSharp/Resolver.cs +++ b/src/Pather.CSharp/Resolver.cs @@ -4,7 +4,9 @@ using System.Threading.Tasks; using System.Reflection; using Pather.CSharp.PathElements; - +using System.Linq.Expressions; +using System.Text.RegularExpressions; + namespace Pather.CSharp { public class Resolver : IResolver @@ -40,7 +42,7 @@ public IList CreatePath(string path) var tempPath = path; while (tempPath.Length > 0) { - var pathElement = createPathElement(tempPath, out tempPath); + var pathElement = CreatePathElement(tempPath, out tempPath); pathElements.Add(pathElement); //remove the dots chaining properties //no PathElement could do this reliably @@ -76,7 +78,7 @@ public object Resolve(object target, IList pathElements) return result; } - private IPathElement createPathElement(string path, out string newPath) + private IPathElement CreatePathElement(string path, out string newPath) { //get the first applicable path element type var pathElementFactory = PathElementFactories.Where(f => f.IsApplicable(path)).FirstOrDefault(); @@ -111,5 +113,20 @@ public object ResolveSafe(object target, string path) return null; } } + + public string ExtractPath(Expression> lambdaExpression) + { + var expressionStr = lambdaExpression.ToString(); + + string pattern = @"\bvalue\([^)]*\)\.(.+)"; + var match = Regex.Match(expressionStr, pattern); + if (match.Success) + { + var result = match.Groups[1].Value.Replace(".get_Item(", "[").Replace(")", "]"); + return result; + } + + throw new ArgumentException($"can not extract path from {expressionStr}"); + } } } diff --git a/test/Pather.CSharp.UnitTests/Pather.CSharp.UnitTests.csproj b/test/Pather.CSharp.UnitTests/Pather.CSharp.UnitTests.csproj index 2cf17d4..b76eefe 100644 --- a/test/Pather.CSharp.UnitTests/Pather.CSharp.UnitTests.csproj +++ b/test/Pather.CSharp.UnitTests/Pather.CSharp.UnitTests.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.0 diff --git a/test/Pather.CSharp.UnitTests/ResolveTests.cs b/test/Pather.CSharp.UnitTests/ResolveTests.cs index e3d56e6..67a8c52 100644 --- a/test/Pather.CSharp.UnitTests/ResolveTests.cs +++ b/test/Pather.CSharp.UnitTests/ResolveTests.cs @@ -1,6 +1,7 @@ using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Pather.CSharp.UnitTests.TestHelper; +using Pather.CSharp.UnitTests.TestModels; using System; using System.Collections; using System.Collections.Generic; @@ -274,6 +275,74 @@ public void IndexerResolution_String_Success() var res = resolver.Resolve(target, path); res.Should().Be("Testabc"); + } + + [TestMethod] + public void ExtractPath_Success() + { + var myclass = new Class() + { + Name = "class 1", + Address = new Address() + { + Email = "abc@china.com" + }, + Students = new List() + { + new Student(){ Name = "Jack", + Subjects = new Subject[] { + new Subject(){Name = "English",Score = 80}, + new Subject(){Name = "Math",Score=90} + }, + Skills = new Dictionary() + { + { "cook",80 }, + { "football",60} + }, + Scores = new Dictionary() + { + {"English", new Subject(){Score = 80} }, + {"Math", new Subject(){Score=90} } + } + }, + new Student(){ Name = "Timi", + Subjects = new Subject[] { + new Subject(){Name = "English",Score = 80}, + new Subject(){Name = "Math",Score=90} + }, + Skills = new Dictionary() + { + { "cook",80 }, + { "football",60} + }, + Scores = new Dictionary() + { + {"English", new Subject(){Score = 80} }, + {"Math", new Subject(){Score=90} } + } + } + } + }; + + IResolver resolver = new Resolver(); + + var case1 = resolver.ExtractPath(() => myclass.Name); + case1.Should().Be("myclass.Name"); + + var case2 = resolver.ExtractPath(() => myclass.Address.Email); + case2.Should().Be("myclass.Address.Email"); + + var case3 = resolver.ExtractPath(() => myclass.Students); + case3.Should().Be("myclass.Students"); + + var case4 = resolver.ExtractPath(() => myclass.Students[0].Name); + case4.Should().Be("myclass.Students[0].Name"); + + var case5 = resolver.ExtractPath(() => myclass.Students[0].Subjects[0].Name); + case5.Should().Be("myclass.Students[0].Subjects[0].Name"); + + var case6 = resolver.ExtractPath(() => myclass.Students[0].Scores["Math"].Score); + case6.Should().Be("myclass.Students[0].Scores[\"Math\"].Score"); } } } diff --git a/test/Pather.CSharp.UnitTests/TestModels/Address.cs b/test/Pather.CSharp.UnitTests/TestModels/Address.cs new file mode 100644 index 0000000..ff214df --- /dev/null +++ b/test/Pather.CSharp.UnitTests/TestModels/Address.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pather.CSharp.UnitTests.TestModels +{ + internal class Address + { + public string Email { get; set; } + } +} diff --git a/test/Pather.CSharp.UnitTests/TestModels/Class.cs b/test/Pather.CSharp.UnitTests/TestModels/Class.cs new file mode 100644 index 0000000..9a1718c --- /dev/null +++ b/test/Pather.CSharp.UnitTests/TestModels/Class.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pather.CSharp.UnitTests.TestModels +{ + internal class Class + { + public string Name { get; set; } + + public List Students { get; set; } + + public Address Address { get; set; } + } +} diff --git a/test/Pather.CSharp.UnitTests/TestModels/Student.cs b/test/Pather.CSharp.UnitTests/TestModels/Student.cs new file mode 100644 index 0000000..a00c935 --- /dev/null +++ b/test/Pather.CSharp.UnitTests/TestModels/Student.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pather.CSharp.UnitTests.TestModels +{ + internal class Student + { + // 姓名 + public string Name { get; set; } + + public Subject[] Subjects { get; set; } + + public Dictionary Skills { get; set; } + + public Dictionary Scores { get; set; } + } +} diff --git a/test/Pather.CSharp.UnitTests/TestModels/Subject.cs b/test/Pather.CSharp.UnitTests/TestModels/Subject.cs new file mode 100644 index 0000000..5669c5e --- /dev/null +++ b/test/Pather.CSharp.UnitTests/TestModels/Subject.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Pather.CSharp.UnitTests.TestModels +{ + internal class Subject + { + public string Name { get; set; } + public double Score { get; set; } + } +}