文章内容
在Idea插件开发过程中,如何方便的读取和修改xml文件,包含自定义的xml文件和部分第三方xml配置。官网知识点介绍及示例
相关知识点及示例
在idea plugin中,一个xml文件对应一个com.intellij.psi.xml.XmlFile。
在ideal plugin中,提供了两种方式操作xml内容:
以如下xml文件为例:
42
239
- xml相关的api查询和操作xml内容
xmlFile.getDocument().getRootTag()
.findFirstSubTag("foo")
.findSubTags("bar")[1]
.getValue()
.getTrimmedText()
- 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和
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
- 选择对应的xsd文件,生成的包名
- 用这种方式生成会SuperClass,记得删除com.intellij.util.xml.DomElement类。
- 生成之后可能还需要修改@NameStrategy来指定命名策略
特别之处
- Maven的pom.xml文件:
pom.xml文件已经被idea自带的jar包已经注册了对应的DomFileDescription:org.jetbrains.idea.maven.dom.MavenDomFileDescription - 以下整理了如何操作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,看懂其实现原理,之后再分析问题之所在。