{"id":767,"date":"2021-05-13T01:53:00","date_gmt":"2021-05-12T12:53:00","guid":{"rendered":"https:\/\/blog.wiseowls.co.nz\/?p=767"},"modified":"2022-03-30T10:28:11","modified_gmt":"2022-03-29T21:28:11","slug":"ef-core-3-1-dynamic-groupby-clause","status":"publish","type":"post","link":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/","title":{"rendered":"EF core 3.1: dynamic GroupBy clause"},"content":{"rendered":"\n<p>Expanding on many ways we can build dynamic clauses for EF execution, let us look at another example. Suppose we&#8217;d like to give our users ability to group items based on a property of their choice.<\/p>\n\n\n\n<p>As usual, our first choice would be to build a LINQ expression as we did with the <a href=\"https:\/\/blog.wiseowls.co.nz\/index.php\/2020\/08\/14\/entity-framework-core-3-1-dynamic-where-clause\/\">WHERE case<\/a>. There&#8217;s however a problem: GroupBy needs an object to use as a grouping key. Usually, we&#8217;d just make an anonymous type, but it is a compile-time luxury we don&#8217;t get with LINQ:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">dbSet.GroupBy(s =&gt; new {s.Col1, s.Col2}); \/\/ not going to fly :\/<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">IL Emit it is then<\/h2>\n\n\n\n<p>So, it seems we are left with no other choice but to go through <code>TypeBuilder<\/code> ordeal (which isn&#8217;t too bad really). One thing to point out here &#8211; we want to create properties that EF will later use for grouping key. This is where ability to interrogate EF as-built model comes very handy. Another important point &#8211; creating a property in fact means creating a private backing field and two special methods for each. We certainly started to appreciate how much the language and runtime do for us: <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">private void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)\n{\n \/\/ really, just generating \"public PropertyType propertyName {get;set;}\"\n\tFieldBuilder fieldBuilder = typeBuilder.DefineField(\"_\" + propertyName, propertyType, FieldAttributes.Private);\n\n\tPropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);\n\tMethodBuilder getPropMthdBldr = typeBuilder.DefineMethod(\"get_\" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);\n\tILGenerator getIl = getPropMthdBldr.GetILGenerator();\n\n\tgetIl.Emit(OpCodes.Ldarg_0);\n\tgetIl.Emit(OpCodes.Ldfld, fieldBuilder);\n\tgetIl.Emit(OpCodes.Ret);\n\n\tMethodBuilder setPropMthdBldr = typeBuilder.DefineMethod(\"set_\" + propertyName,\n\t\t  MethodAttributes.Public |\n\t\t  MethodAttributes.SpecialName |\n\t\t  MethodAttributes.HideBySig,\n\t\t  null, new[] { propertyType });\n\n\tILGenerator setIl = setPropMthdBldr.GetILGenerator();\n\tLabel modifyProperty = setIl.DefineLabel();\n\tLabel exitSet = setIl.DefineLabel();\n\n\tsetIl.MarkLabel(modifyProperty);\n\tsetIl.Emit(OpCodes.Ldarg_0);\n\tsetIl.Emit(OpCodes.Ldarg_1);\n\tsetIl.Emit(OpCodes.Stfld, fieldBuilder);\n\n\tsetIl.Emit(OpCodes.Nop);\n\tsetIl.MarkLabel(exitSet);\n\tsetIl.Emit(OpCodes.Ret);\n\n\tpropertyBuilder.SetGetMethod(getPropMthdBldr);\n\tpropertyBuilder.SetSetMethod(setPropMthdBldr);\n}<\/code><\/pre>\n\n\n\n<p>after we&#8217;ve sorted this out &#8211; it&#8217;s pretty much the same approach as with any other dynamic LINQ expression: we need to build something like <code>DbSet.GroupBy(s => new dynamicType {col1 = s.q1, col2 = s.q2})<\/code>. There&#8217;s a slight issue with this lambda however &#8211; it returns <code>IGrouping&lt;dynamicType, TElement><\/code> &#8211; and since outside code has no idea of the dynamic type &#8211; there&#8217;s no easy way to work with it (unless we want to keep reflecting). I thought it might be easier to build a <code>Select<\/code> as well and return a <code>Count<\/code> against each instance of dynamic type. Luckily, we only needed a count, but other aggregations work in similar fashion. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Finally<\/h2>\n\n\n\n<p>I arrived at the following code to generate required expressions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp\">public static IQueryable&lt;Tuple&lt;object, int&gt;&gt; BuildExpression&lt;TElement&gt;(this IQueryable&lt;TElement&gt; source, DbContext context, List&lt;string&gt; columnNames)\n{\n\tvar entityParameter = Expression.Parameter(typeof(TElement));\n\tvar sourceParameter = Expression.Parameter(typeof(IQueryable&lt;TElement&gt;));\n\n\tvar model = context.Model.FindEntityType(typeof(TElement)); \/\/ start with our own entity\n\tvar props = model.GetPropertyAccessors(entityParameter); \/\/ get all available field names including navigations\t\t\t\n\n\tvar objectProps = new List&lt;Tuple&lt;string, Type&gt;&gt;();\n\tvar accessorProps = new List&lt;Tuple&lt;string, Expression&gt;&gt;();\n\tvar groupKeyDictionary = new Dictionary&lt;object, string&gt;();\n\tforeach (var prop in props.Where(p =&gt; columnNames.Contains(p.Item3)))\n\t{\n\t\tvar propName = prop.Item3.Replace(\".\", \"_\"); \/\/ we need some form of cross-reference, this seems to be good enough\n\t\tobjectProps.Add(new Tuple&lt;string, Type&gt;(propName, (prop.Item2 as MemberExpression).Type));\n\t\taccessorProps.Add(new Tuple&lt;string, Expression&gt;(propName, prop.Item2));\n\t}\n\n\tvar groupingType = BuildGroupingType(objectProps); \/\/ build new type we'll use for grouping. think `new Test() { A=, B=, C= }`\n\n\t\/\/ finally, we're ready to build our expressions\n\tvar groupbyCall = BuildGroupBy&lt;TElement&gt;(sourceParameter, entityParameter, accessorProps, groupingType); \/\/ source.GroupBy(s =&gt; new Test(A = s.Field1, B = s.Field2 ... ))\n\tvar selectCall = groupbyCall.BuildSelect&lt;TElement&gt;(groupingType); \/\/ .Select(g =&gt; new Tuple&lt;object, int&gt; (g.Key, g.Count()))\n\t\n\tvar lambda = Expression.Lambda&lt;Func&lt;IQueryable&lt;TElement&gt;, IQueryable&lt;Tuple&lt;object, int&gt;&gt;&gt;&gt;(selectCall, sourceParameter);\n\treturn lambda.Compile()(source);\n}\n\nprivate static MethodCallExpression BuildSelect&lt;TElement&gt;(this MethodCallExpression groupbyCall, Type groupingAnonType) \n{\t\n\tvar groupingType = typeof(IGrouping&lt;,&gt;).MakeGenericType(groupingAnonType, typeof(TElement));\n\tvar selectMethod = QueryableMethods.Select.MakeGenericMethod(groupingType, typeof(Tuple&lt;object, int&gt;));\n\tvar resultParameter = Expression.Parameter(groupingType);\n\n\tvar countCall = BuildCount&lt;TElement&gt;(resultParameter);\n\tvar resultSelector = Expression.New(typeof(Tuple&lt;object, int&gt;).GetConstructors().First(), Expression.PropertyOrField(resultParameter, \"Key\"), countCall);\n\n\treturn Expression.Call(selectMethod, groupbyCall, Expression.Lambda(resultSelector, resultParameter));\n}\n\nprivate static MethodCallExpression BuildGroupBy&lt;TElement&gt;(ParameterExpression sourceParameter, ParameterExpression entityParameter, List&lt;Tuple&lt;string, Expression&gt;&gt; accessorProps, Type groupingAnonType) \n{\n\tvar groupByMethod = QueryableMethods.GroupByWithKeySelector.MakeGenericMethod(typeof(TElement), groupingAnonType);\n\tvar groupBySelector = Expression.Lambda(Expression.MemberInit(Expression.New(groupingAnonType.GetConstructors().First()),\n\t\t\taccessorProps.Select(op =&gt; Expression.Bind(groupingAnonType.GetMember(op.Item1)[0], op.Item2))\n\t\t), entityParameter);\n\n\treturn Expression.Call(groupByMethod, sourceParameter, groupBySelector);\n}\n\nprivate static MethodCallExpression BuildCount&lt;TElement&gt;(ParameterExpression resultParameter)\n{\n\tvar asQueryableMethod = QueryableMethods.AsQueryable.MakeGenericMethod(typeof(TElement));\n\tvar countMethod = QueryableMethods.CountWithoutPredicate.MakeGenericMethod(typeof(TElement));\n\n\treturn Expression.Call(countMethod, Expression.Call(asQueryableMethod, resultParameter));\n}<\/code><\/pre>\n\n\n\n<p>And the full working version is on my GitHub<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Expanding on many ways we can build dynamic clauses for EF execution, let us look at another example. Suppose we&#8217;d like to give our users ability to group items based on a property of their choice. As usual, our first choice would be to build a LINQ expression as we did with the WHERE case. &hellip; <a href=\"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;EF core 3.1: dynamic GroupBy clause&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[11],"tags":[23,59],"class_list":["post-767","post","type-post","status-publish","format-standard","hentry","category-dev","tag-ef-core","tag-sql"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.4 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>EF core 3.1: dynamic GroupBy clause - Timur and associates<\/title>\n<meta name=\"description\" content=\"We have already looked at ways to build user-driven WHERE clauses for EF Core. Now is the turn of dynamic GROUP BY.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"EF core 3.1: dynamic GroupBy clause - Timur and associates\" \/>\n<meta property=\"og:description\" content=\"We have already looked at ways to build user-driven WHERE clauses for EF Core. Now is the turn of dynamic GROUP BY.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/\" \/>\n<meta property=\"og:site_name\" content=\"Timur and associates\" \/>\n<meta property=\"article:published_time\" content=\"2021-05-12T12:53:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-03-29T21:28:11+00:00\" \/>\n<meta name=\"author\" content=\"timur\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@TimurKh\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/\"},\"author\":{\"name\":\"timur\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#\\\/schema\\\/person\\\/34d0ed30d573b5bc317ea990bd2e0c59\"},\"headline\":\"EF core 3.1: dynamic GroupBy clause\",\"datePublished\":\"2021-05-12T12:53:00+00:00\",\"dateModified\":\"2022-03-29T21:28:11+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/\"},\"wordCount\":300,\"keywords\":[\"ef-core\",\"sql\"],\"articleSection\":[\"Development\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/\",\"url\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/\",\"name\":\"EF core 3.1: dynamic GroupBy clause - Timur and associates\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#website\"},\"datePublished\":\"2021-05-12T12:53:00+00:00\",\"dateModified\":\"2022-03-29T21:28:11+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#\\\/schema\\\/person\\\/34d0ed30d573b5bc317ea990bd2e0c59\"},\"description\":\"We have already looked at ways to build user-driven WHERE clauses for EF Core. Now is the turn of dynamic GROUP BY.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/index.php\\\/2021\\\/05\\\/13\\\/ef-core-3-1-dynamic-groupby-clause\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"EF core 3.1: dynamic GroupBy clause\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#website\",\"url\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/\",\"name\":\"Timur and associates\",\"description\":\"Notes of an IT contractor\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/blog.wiseowls.co.nz\\\/#\\\/schema\\\/person\\\/34d0ed30d573b5bc317ea990bd2e0c59\",\"name\":\"timur\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg\",\"caption\":\"timur\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/TimurKh\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"EF core 3.1: dynamic GroupBy clause - Timur and associates","description":"We have already looked at ways to build user-driven WHERE clauses for EF Core. Now is the turn of dynamic GROUP BY.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/","og_locale":"en_US","og_type":"article","og_title":"EF core 3.1: dynamic GroupBy clause - Timur and associates","og_description":"We have already looked at ways to build user-driven WHERE clauses for EF Core. Now is the turn of dynamic GROUP BY.","og_url":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/","og_site_name":"Timur and associates","article_published_time":"2021-05-12T12:53:00+00:00","article_modified_time":"2022-03-29T21:28:11+00:00","author":"timur","twitter_card":"summary_large_image","twitter_creator":"@TimurKh","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/#article","isPartOf":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/"},"author":{"name":"timur","@id":"https:\/\/blog.wiseowls.co.nz\/#\/schema\/person\/34d0ed30d573b5bc317ea990bd2e0c59"},"headline":"EF core 3.1: dynamic GroupBy clause","datePublished":"2021-05-12T12:53:00+00:00","dateModified":"2022-03-29T21:28:11+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/"},"wordCount":300,"keywords":["ef-core","sql"],"articleSection":["Development"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/","url":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/","name":"EF core 3.1: dynamic GroupBy clause - Timur and associates","isPartOf":{"@id":"https:\/\/blog.wiseowls.co.nz\/#website"},"datePublished":"2021-05-12T12:53:00+00:00","dateModified":"2022-03-29T21:28:11+00:00","author":{"@id":"https:\/\/blog.wiseowls.co.nz\/#\/schema\/person\/34d0ed30d573b5bc317ea990bd2e0c59"},"description":"We have already looked at ways to build user-driven WHERE clauses for EF Core. Now is the turn of dynamic GROUP BY.","breadcrumb":{"@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.wiseowls.co.nz\/index.php\/2021\/05\/13\/ef-core-3-1-dynamic-groupby-clause\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.wiseowls.co.nz\/"},{"@type":"ListItem","position":2,"name":"EF core 3.1: dynamic GroupBy clause"}]},{"@type":"WebSite","@id":"https:\/\/blog.wiseowls.co.nz\/#website","url":"https:\/\/blog.wiseowls.co.nz\/","name":"Timur and associates","description":"Notes of an IT contractor","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.wiseowls.co.nz\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.wiseowls.co.nz\/#\/schema\/person\/34d0ed30d573b5bc317ea990bd2e0c59","name":"timur","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg","url":"https:\/\/secure.gravatar.com\/avatar\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/23d55e17d4f0990ee4d12bc6e5dcfb58a292934efd62a185756876379e780b16?s=96&r=pg","caption":"timur"},"sameAs":["https:\/\/x.com\/TimurKh"]}]}},"_links":{"self":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts\/767","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/comments?post=767"}],"version-history":[{"count":10,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts\/767\/revisions"}],"predecessor-version":[{"id":879,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/posts\/767\/revisions\/879"}],"wp:attachment":[{"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/media?parent=767"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/categories?post=767"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.wiseowls.co.nz\/index.php\/wp-json\/wp\/v2\/tags?post=767"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}