Idea plugin之Xml文件读取和操作

文章内容

在Idea插件开发过程中,如何方便的读取和修改xml文件,包含自定义的xml文件和部分第三方xml配置。官网知识点介绍及示例

相关知识点及示例

在idea plugin中,一个xml文件对应一个com.intellij.psi.xml.XmlFile。
在ideal plugin中,提供了两种方式操作xml内容:
以如下xml文件为例:


    
        42
        239
    

  1. xml相关的api查询和操作xml内容
xmlFile.getDocument().getRootTag()
            .findFirstSubTag("foo")
      .findSubTags("bar")[1]
      .getValue()
      .getTrimmedText()
  1. Idea plugin中使用动态代理技术,提供了一种自定义xml内容层级结构的方式操作xml内容。
    2.1定义interface:
interface Root extends com.intellij.util.xml.DomElement {
    Foo getFoo();
}
interface Foo extends com.intellij.util.xml.DomElement {
    List getBars();
}
interface Bar extends com.intellij.util.xml.DomElement {
    String getValue();
}

2.2 创建并注册DomFileDescription:
创建DomFileDescription:

public class CustomDomFileDescription extends DomFileDescription {

    public CustomDomFileDescription() {
        super(Root.class, "root", "http://x.y.z/xml/4.0.0");
    }
}

注册DomFileDescription:


    
    
  

2.3 操作代码:

DomManager manager = DomManager.getDomManager(e.getProject());
XmlFile file = // 读取的file;      
Root root = manager.getFileElement(file, Root.class).getRootElement();
List bars = root.getFoo().getBars();
if (bars.size() > 1) {
    String s = bars.get(1).getValue();
  Messages.showInfoMessage("值为:" + s, "提示");
}   

两种方式对比:

第一种方式每个api都可能返回null,当层级结构比较多是,需要很多判断null。而第二种方式则不会。

第二种方式的相关知识点:

  • xml标签对应一个interface,继承:com.intellij.util.xml.DomElement
  • 对于42这种,可以定义如下方法来获取和设置标签内的内容:
String getValue();
void setValue(String s);

也可以使用@TagValue标注获取和设置标签内的内容的方法:

@TagValue
String getTagValue();
@TagValue
void setTagValue(String s);

之上的例子返回值和设置值的类型都是String。这是很自然的,因为XML表示文本格式,而标记内容总是文本。但有时您可能希望使用整数、布尔值、枚举甚至类名(当然,它们将被表示为PsiClass)和更泛型的Java类型(PsiType)进行操作。在这种情况下,您只需要将方法中的类型更改为您需要的类型,一切都会正常工作。

对于一些自定义类型T,需要在标签内内容相互转换时,需要用@Convert指定转换器,转换器继承Converter

比如在xml文件中增加beanTag:



    
        42
        239
    
    {"id":1,"name":"zhu"}

增加类:Bean.class

public class Bean{
    private Long id;
    private String name;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

新增Converter:Bean和中的String相互转换

public class BeanConverter extends Converter {
    @Override
    public @Nullable Bean fromString(@Nullable @NonNls String s, ConvertContext context) {
        if(StringUtils.isNotEmpty(s)){
            return JSON.parseObject(s, Bean.class);
        }
        return null;
    }
    @Override
    public @Nullable String toString(@Nullable Bean bean, ConvertContext context) {
        if(bean == null){
            return "";
        }
        return JSON.toJSONString(bean);
    }
}

配置:

@NameStrategy(JavaNameStrategy.class)
public interface Root extends com.intellij.util.xml.DomElement{
    Foo getFoo();
    @Convert(BeanConverter.class)
    GenericDomValue getBeanTag();
}

读取和修改代码:

DomManager manager = DomManager.getDomManager(e.getProject());
WriteCommandAction.runWriteCommandAction(e.getProject(), ()->{
  Root root = manager.getFileElement(xmlFile, Root.class).getRootElement();
  Bean bean = root.getBeanTag().getValue();
  Messages.showInfoMessage(bean.getName(), "自定义bean");
  // 修改值
  bean.setName("zhu-new");
  root.getBeanTag().setValue(bean);
}

xml标签属性操作:

bean中定义getter方法:返回值类型使用:GenericAttributeValue

@NameStrategy(JavaNameStrategy.class)
public interface Root extends com.intellij.util.xml.DomElement{
    Foo getFoo();
    @Convert(BeanConverter.class)
    GenericDomValue getBeanTag();
    @Attribute("name")
    GenericAttributeValue getName();
}

DomNameStrategy:属性名与xml标签之间的转换策略。通过@NameStrategy在定义的interface上指定策略。

@NameStrategy(JavaNameStrategy.class)
public interface Root extends com.intellij.util.xml.DomElement{
}

目前支持两种:

  • HyphenNameStrategy:以中划线【-】分隔。getSomeTag()->
  • JavaNameStrategy:驼峰格式。getSomeTag()->

删除标签或属性:.undefine()

// 删除name属性
root.getName().undefine();
// 删除beanTag标签
root.getBeanTag().undefine();
// 删除foo标签
root.getFoo().undefine();

列表相关的操作:

在Foo中新增两个addBar方法,一个是放在最后,一个是指定下标

public interface Foo extends com.intellij.util.xml.DomElement{
    List getBars();
    @SubTagList("bar")
    Bar addBar();
    @SubTagList("bar")
    Bar addBar(int idx);
}

使用:

root.getFoo().addBar().setValue("我是新增的");
root.getFoo().addBar(1).setValue("我是插入的");

修改前:



    
        42
        239
    
    {"id":1,"name":"zhu-new"}

修改后:



    
        42
        我是插入的
        239
        我是新增的
    
    {"id":1,"name":"zhu-new"}

下面将介绍idea中支持的一种工具,用XSD/DTD生成上面用到的一些interface。

步骤:

  • Help | Edit Custom Properties 后添加:idea.is.internal=true 后重启
  • 菜单栏中选择:Tools | Internal Actions | DevKit | Generate DOM Model


    image
  • 选择对应的xsd文件,生成的包名
    image
  • 用这种方式生成会SuperClass,记得删除com.intellij.util.xml.DomElement类。
  • 生成之后可能还需要修改@NameStrategy来指定命名策略

特别之处

  1. Maven的pom.xml文件:
    pom.xml文件已经被idea自带的jar包已经注册了对应的DomFileDescription:org.jetbrains.idea.maven.dom.MavenDomFileDescription
  2. 以下整理了如何操作pom.xml:
  • plugin.xml中增加:

org.jetbrains.idea.maven

  • build.gradle中:
intellij {
    plugins = ["org.jetbrains.idea.maven"]
}
  • 获取根标签:
MavenDomProjectModel mavenProject = DomManager.getDomManager(project)
.getFileElement(pomFile, MavenDomProjectModel.class)

              .getRootElement();
  • MavenDomProjectModel中就有pom.xml中所有的标签。

总结

  • 官网文章
  • 学习最好的方式就是开始写起来,遇到问题后可以尝试debug,看懂其实现原理,之后再分析问题之所在。

你可能感兴趣的:(Idea plugin之Xml文件读取和操作)