我正在尝试扩展自定义Sitecore命令,以确定当前项是否具有与某个模板ID匹配的父项。
我知道如果项目是上下文,或./ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}']
/sitecore/content/home/full/path/to/the-item/ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}']
一样简单
不幸的是,商品路径包含需要像/sitecore/content/home/full/path/to/#the-item#/ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}']
一样进行转义的短划线。
但是,理想情况下,我只想使用项目的完整路径,因为它可以item.Paths.FullPath
使用。
给定一个项目,编写包含它的完整路径的查询以及转义可能包含在其中的任何破折号的最佳方法是什么?
答案 0 :(得分:5)
假设您有Sitecore.Data.Items.Item
(称为item
)的引用,并且您希望找到具有给定Sitecore.Data.ID
(称为id
)的祖先,则有您可以通过多种方式访问祖先。
在没有任何自定义库的典型Sitecore设置中,我通常会使用一些Linq,以避免XPath中的编码问题。
ancestor =
item
.Axes
.GetAncestors()
.FirstOrDefault(ancestor => ancestor.TemplateID == id);
Closest
我使用定制的Sitecore开发框架,涉及各种扩展方法和自定义项目生成。为了避免在过滤之前访问所有祖先的开销,我会使用Closest
扩展方法:
public static Item Closest(this Item source, Func<Item, bool> test)
{
Item cur;
for (cur = source; cur != null; cur = cur.Parent)
if (test(cur))
break;
return cur;
}
访问祖先将是:
ancestor =
item
.Closest(ancestor => ancestor.TemplateID == id);
(实际上,我通常使用看起来像的代码)
ancestor = (ITemplateNameItem) item.Closest(Is.Type<ITemplateNameItem>);
我通常会避免使用XPath并且仅将其用作最后的工具,因为它经常使代码更难阅读,引入编码问题,例如您在此问题中遇到的问题,并且对数字有严格的限制可以退回的物品。
也就是说,Sitecore有许多可用于使用XPath进行搜索的工具,在某些情况下 可以简化操作。
修复包含空格的项目路径的技巧是:不要使用项目路径。
相反,您可以安全地使用项目的ID,不需要更多上下文,因为它是对项目的绝对引用。它也保证遵循特定的格式。
var query =
string.Format(
"//{0}/ancestor::*[@@templateid='{1}']",
item.ID.ToString(),
id.ToString());
/* alternatively
var query =
string.Format(
"{0}/ancestor::*[@@templateid='{1}']",
item.Paths.LongID,
id.ToString());
*/
ancestor =
item
.Database
.SelectSingleItem(query);
Sitecore.Data.Query.Query
正如我之前提到的,Sitecore有许多工具可用于使用XPath进行搜索。其中一个工具是Sitecore.Data.Query.Query
。 SelectItems
和SelectSingleItem
方法有其他可选参数,其中一个是Sitecore.Data.Items.Item
contextNode
。
将项目作为第二个参数传递使用该项目作为XPath查询的上下文。
var query =
string.Format(
"./ancestor::*[@@templateid='{0}']",
id.ToString());
ancestor =
Sitecore
.Data
.Query
.Query
.SelectSingleItem(query, item);
答案 1 :(得分:3)
我从未见过任何Sitecore工具类或任何其他开箱即用的代码,可以满足您的需求。有趣的是,Sitecore确实有一个返回false
的util方法(是的,它确实返回false
),但没有一个方法可以转义项路径,因此可以在查询中使用它。
你可以在这里找到符合你想要的代码(由Anders Laub编写):https://blog.istern.dk/2014/10/29/escaping-dashes-in-sitecore-queries-datasource-query-update/
如果你懒得点击链接,我已经复制到这里的代码:
private string EscapeItemNamesWithDashes(string queryPath)
{
if (!queryPath.Contains("-"))
return queryPath;
var strArray = queryPath.Split(new char[] { '/' });
for (int i = 0; i < strArray.Length; i++)
{
if (strArray[i].IndexOf('-') > 0)
strArray[i] = "#" + strArray[i] + "#";
}
return string.Join("/", strArray);
}
如果你想逃避空格字符,你可以试试这个正则表达式:
string escapedPath = Regex.Replace(myItem.Paths.FullPath, "[^/]*[- ][^/]*", "#$0#");
另外你应该记住Sitecore中有一些保留字也应该被转义。这些是(从http://www.newguid.net/sitecore/2012/escape-characterswords-in-a-sitecore-query/复制而来):
答案 2 :(得分:2)
Sitecore中没有可以逃脱Sitecore查询路径的公共帮助程序方法。您需要手动实现转义逻辑。
我在管道处理器Sitecore.Pipelines.GetLookupSourceItems.ProcessDefaultSource
中的Sitecore.Kernel.dll中找到了一些代码。我不得不重做它,以便包含:
和[...]
的复杂选择器不会被转义:
static string EscapeQueryPath(string queryPath)
{
string[] strArray = queryPath.Split('/');
for (int i = 0; i < strArray.Length; ++i)
{
string part = strArray[i];
if ((part.IndexOf(' ') >= 0 || part.IndexOf('-') >= 0)
&& part.IndexOf(':') == -1
&& part.IndexOf('[') == -1
&& !part.StartsWith("#", StringComparison.InvariantCulture)
&& !part.EndsWith("#", StringComparison.InvariantCulture))
{
strArray[i] = '#' + part + '#';
}
}
return string.Join("/", strArray);
}
请注意此算法:
/#item-name#/
不会变成/##item-name##/
。ancestor::*[...]
)。这是另一种逃避方法。它将与上面的代码具有完全相同的结果。
string path = "./my-item/ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}']";
string result = Regex.Replace(str, "/([^/#\\[\\:]*[- ][^/#\\[\\:]*(?=($|/)))", "/#$1#");
// result: ./#my-item#/ancestor::*[@@templateid='{26710865-F082-4714-876B-D5E1F386792F}']
这个更短,但很可能有点慢。
答案 3 :(得分:0)
我同意之前的答案,但是对于查询而言,还有一些必须转义的内容 - 带有单词'或'&amp; '和'以及以数字开头的单词。我使用了类似的代码:
string _path = "/path/to-item/12name and qwe/test"; // your path
string[] pathParts = _path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
string escapedPath = string.Join("/", pathParts.Select(p =>
{
if (p.Contains("and") || p.Contains("or") || p.Contains("-") || char.IsDigit(p[0]))
{
return "#" + p + "#";
}
return p;
}));