Roslyn+T4+EnvDTE项目完全自动化(7) ——光标取元素(XPath)


(视频演示)

获取xml XPath

  • 有些很复杂的xml 嵌套多个命名空间,手写 XPath 经常出错,想一步到位

 按Alt+C调用GetXPath

  •  生成代码(选择节点)
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("x", "http://schemas.microsoft.com/ado/2009/11/edmx");
namespaceManager.AddNamespace("x1", "http://schemas.microsoft.com/ado/2009/11/edm/ssdl");
//带命名空间
var elements = doc.XPathSelectElements("/x:Edmx/x:Runtime/x:StorageModels/x1:Schema/x1:EntityContainer/x1:EntitySet", namespaceManager);
//
不带命名空间
var elements1 = doc.XPathSelectElements("/Edmx/Runtime/StorageModels/Schema/EntityContainer/EntitySet", namespaceManager);
  • 生成代码(选择属性)
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("x", "http://schemas.microsoft.com/ado/2009/11/edmx");
namespaceManager.AddNamespace("x1", "http://schemas.microsoft.com/ado/2009/11/edm/ssdl");
namespaceManager.AddNamespace("x2", "http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator");
//带命名空间
var elements = doc.XPathSelectElements("/x:Edmx/x:Runtime/x:StorageModels/x1:Schema/x1:EntityContainer/x1:EntitySet", namespaceManager);
//不带命名空间
var elements1 = doc.XPathSelectElements("/Edmx/Runtime/StorageModels/Schema/EntityContainer/EntitySet", namespaceManager);
//不带命名空间XPath
var elements2 = doc.XPathSelectElements("/x:Edmx/x:Runtime/x:StorageModels/x1:Schema/x1:EntityContainer/x1:EntitySet[@x2:Type='Tables']", namespaceManager); XNamespace ns = "http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"; var attribute = elem.Attribute(ns + "Type");
  • 怎么实现
  1. 用DTE获取光标的行、列号
  2. 行列转换成Roslyn的Microsoft.CodeAnalysis.Text.TextSpan span
  3. 设置xml解析保留行号:XDocument.Parse(text, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
  4. 判断xml节点与span相交的元素
  • 核心代码
        public void FindElem()
        {
            var span = dte.GetTextSpanner(ProjectItem.GetFileName()).GetDteTextSpan();

            foreach (var element in Document.Descendants())
            {
                {
                    var lineInfo = (IXmlLineInfo)element;
                    if (!lineInfo.HasLineInfo())
                    {
                        continue;
                    }

                    var r = GetLineInfos(element);
                    if (span.iStartLine <= r.End && span.iEndLine >= r.Start)
                    {
                        var name = element.GetNameWithPrefix();
                        if (span.iEndLine > span.iStartLine || span.iStartIndex >= lineInfo.LinePosition && span.iStartIndex <= lineInfo.LinePosition + name.Length)
                        {
                            Elem = element;
                        }
                    }
                }
                foreach (var attribute in element.Attributes())
                {
                    var lineInfo = (IXmlLineInfo)attribute;
                    if (!lineInfo.HasLineInfo()) continue;

                    if (lineInfo.LineNumber >= span.iStartLine && lineInfo.LineNumber <= span.iEndLine)
                    {
                        var name = attribute.ToString();
                        var yStart = lineInfo.LinePosition;
                        var yEnd = yStart + name.Length - 1;
                        if (span.iStartLine != span.iEndLine || span.iStartIndex <= yEnd && span.iEndIndex >= yStart)
                        {
                            Elem = element;
                            Attributes.Add(attribute);
                        }
                    }
                }

                if (Elem != null)
                {
                    goto Return;
                }
            }

            throw new Exception("not find XElement");
            Return:
            var items = new List() { Elem, };
            foreach (var element in Elem.ElementsAfterSelf())
            {
                var lineInfo = (IXmlLineInfo)element;
                if (!lineInfo.HasLineInfo()) continue;
                if (lineInfo.LineNumber < span.iEndLine)
                {
                    items.Add(element);
                }
                else if (lineInfo.LineNumber == span.iEndLine)
                {
                    if (span.iEndIndex >= lineInfo.LinePosition)
                    {
                        items.Add(element);
                    }
                }
            }

            Elems = items;
        }