Create new .json file in specific directory

Good Morning every one, 

I would like to know, if there is a way to create a JSON file and insert it in a specific directory, like using template files to create java classes? 

and another point is the fact that, how can I create it in a specific directory like (res/raw/test.json)

thank you in advance

28 comments
Comment actions Permalink

Good morning. You can create file templates for any kind of file you want using File -> Settings -> Editor -> File and Code Templates. There already exist several samples of JSON file templates:

As about creating them in a specific directory, could you please clarify a bit what do you need exactly - to show corresponding file templates only under particular directories, or to create the full hierarchy of nested directories containing this file, or to automatically generate the file in some certain scenario?

Thanks in advance for any further info.

0
Comment actions Permalink

Thanks for your hints. 

In particular, I started to develop an android studio plugin to automatically generate some implementation code and add it to the developer application. the work that I need to do in one part of this plugin is to create a JSON file like the following:

{
"User_name":"test",
"https_required": true,
....
}

so, I would like to know, How can I create this programmatically, and then I need to put this file in a resource directory of the application as the one you can see in the attached image (res\raw)

Indeed, I need to create the JSON file programmatically and then create a sub-directory inside res directory with the name of i.e test and insert the JSON file there. 

thanks in advance for your help 

0
Comment actions Permalink

If you want to add a file template from your plugin, you can register a template in your plugin.xml:

<internalFileTemplate name="my_json"/>

You place your template under "fileTemplates\internal" in your resources directory, with name like "my_json.json.ft". (And add one more file "my_json.json.html" if you want to provide a description.)

And then you may use com.intellij.ide.actions.CreateFileFromTemplateAction#createFileFromTemplate() in your action invocation. Use FileTemplateManager.getInstance(project).getInternalTemplate to get the template object.

 

Now it depends only on how you want to invoke your template.

Do you want to have an action in some context menu, or to expand it when the project is being created?

0
Comment actions Permalink

Actually, I have an action that in some context menu. so, I would like this functionality as well, in the aforementioned part. so, How can I inject it into the specified directory by using the aforementioned method? 

Regards. 

0
Comment actions Permalink

>Actually, I have an action that in some context menu.

In this case, just invoke CreateFileFromTemplateAction#createFileFromTemplate inside you action, it allows to specify the directory where your file is to be created.

You may also override isAvailable for your action, if you want it to be shown only in specific contexts.

0
Comment actions Permalink

thank you very much for your help. I will try that. 

 

0
Comment actions Permalink

Dear Anton, as you suggest, I take a look at this part "CreateFileFromTemplateAction#createFileFromTemplate" but the method take a PsiDirectory as an variable, so my question is that, how can I achieve this? 

Best Regards


0
Comment actions Permalink

I would like to ask another question, if I want to add an XML file can I follow the same procedure by using this class as well? 

thank you in advance

0
Comment actions Permalink

com.intellij.psi.PsiManager#findDirectory, com.intellij.openapi.vfs.VirtualFileManager#refreshAndFindFileByUrl, com.intellij.openapi.util.io.FileUtil#toSystemIndependentName, com.intellij.openapi.vfs.VirtualFileManager#constructUrl, com.intellij.openapi.vfs.VirtualFileSystem#getProtocol for LocalFileSystem

Our platform has many levels of abstraction, sorry. :) These APIs should be enough for getting PsiDirectory having its file system path. In case of any further questions - don't hesitate to ask.

 

Yes, the same can be done for any kind of files, including XML files.

0
Comment actions Permalink

I am sorry to disturb you again with my questions. 

String pathToFile = "/Users/amirsharif/AndroidStudioProjects/test2/app/src/main/res/layout/activity_main.xml";
VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(pathToFile);
FileTemplate fileTemplate = FileTemplateManager.getInstance(myProject).getInternalTemplate("TokenActivity");
resourceDirectory = psiManager.findDirectory(virtualFile);
CreateFileFromTemplateAction.createFileFromTemplate(fileTemplate, resourceDirectory);

I tried to add the xml file by using the upper code, but I got an error that is said " can not resolve method createFileFromTemplate.
0
Comment actions Permalink

No problem, feel free to ask your questions any time you need.

There exists no such method signature as you tried to use, that's correct.

IntelliJ community is an open-source product, so you may always consult source code in case of doubt. For instance, here you see all the available overloads for `createFileFromTemplate`:

https://github.com/JetBrains/intellij-community/blob/master/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateAction.java

We have stable branches numbered with the last two digits of year + one digit for minor update number, in case you want to see code corresponding to a particular release. So, if you want to browse source code for stable release 2018.2, for example, you should switch to branch '182'. For 2018.3, switch to branch '183', etc.

0
Comment actions Permalink

Actually, I did the same and I used one of the overload in the aforementioned class

protected PsiFile createFileFromTemplate(final String name, final FileTemplate template, final PsiDirectory dir). 

 

so, I don't know what is the problem, and actually the first value can be null (final string name). 

0
Comment actions Permalink

The method you're trying to invoke in static context is not static. :) Please use a static overload.

0
Comment actions Permalink

Good Morning, 

I solved the problem with thanks to your hints. now, I got an error here, when I try to get the PsiDirectory by using the following code:

psiManager = psiManager.getInstance(myProject);

String pathToFile = "/Users/amirsharif/IdeaProjects/MyApplication/app/src/main/res/layout/activity_main.xml";

VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(pathToFile);

resourceDirectory = psiManager.findDirectory(virtualFile);

when I tried to debug, I understand that it returns null. 

0
Comment actions Permalink

Good morning.

That's true that it returns null. This happens because you pass there a VirtualFile that is not a directory.

VirtualFile is an abstraction that encompasses both files and directories. So, when you have a VirtualFile for your file, just get its parent VirtualFile (`getParent` method) which is a directory, and then pass it to psiManager.findDirectory.

So, findDirectory doesn't find a parent directory for a file. It finds a PsiDirectory for a VirtualFile representing a directory.

(As for getting the project path, and handling project opening you may take a look at ProjectUtils.kt.)

0
Comment actions Permalink

oh, I see. I am so careless. now, it works. Thanks for the hint. I will take a look to the other class in order to understand how to take the project path. 

thanks

0
Comment actions Permalink

Hello, 
I tried to use "com.org.jetbrains.android" packages inside the plugin to be able to edit manifest file and some other purposes, but I got an error that Error:(18, 42) java: package org.jetbrains.android.dom.manifest does not exist. 

So, should I change the plugin.xml <depends> part?

Thanks in advance

0
Comment actions Permalink

Yes, you're right. You should add a dependency, <depends> with the ID of the referenced plugin. We have an article covering that.

0
Comment actions Permalink

Hello there, 

I have a new question with regard to JSON. I would like to know, is it possible to have a JSON template and set the values in the template dynamically from the inputs that are fetched from the GUI? I mean, like to have a place holder in the template file that gets its value from the inputs that are read from the GUI? for example:

``

{

    Name: "${val1}",
    Surename:"${val2}"

}

`` 

Thank you in advance 

0
Comment actions Permalink

Hello Asharif,

Thanks for your question.

Could you please explain a bit more what do you mean by "from the GUI"?

Does our standard live templates mechanism suit your purposes, and you just want to add a live template by your plugin? Do you want to add your own macro for live template variables? Or do you want something more complicated?

Check this settings page: Settings -> Editor -> Live Templates. Create a new template with text:

```

{
  Name: "$val1$",
  Surname:"$val2$"
}

```

After doing that, press "Edit variables". You can choose a macro expression executed for each template variable:

 

If this is what you want, and you want to achieve the same programmatically - I can tell you what to do.

If you want something more complicated - please explain a bit more which kind of a UI do you want for editing the template variables.

Thanks.

0
Comment actions Permalink

Hi, 

Thanks for your answer. I think I need something more complicated. I have a plugin for automatic code generation. In one part of the plugin, I need to insert a JSON configuration file into the developer application. my plugin has a Graphical user interface, that asks some information from the developer. I need to read this input for example and then insert it into the JSON file, besides this, there is some other information, such as name, and surname, etc that are saved inside another class. so, in general, I need to insert this information as a value for each key inside the JSON. 

thanks in advance 

Amir

0
Comment actions Permalink

if I want to give you a better example please take a look at the following screenshots

consider the JSON template as the first picture, then I want to fill the values by the input that I get from the user in the second picture


the value of Developer Domain should be added inside the JSON file as an input for the "authorization_endpoint_uri". 

If it is not clear enough, please let me know. 

thanks in advance 

Amir

0
Comment actions Permalink

Hello Anton, 

Do you get into any idea? 

thanks

0
Comment actions Permalink

Hello Amir,

Sorry, was unable to reply yesterday.

I see what you need, you need to create your own file/code template, then you can use Velocity template language in your template, and update the template variables from your code.

Check the existing templates here (just to see how that works):

 

For an example on how to create such templates, check this for templates themselves (in your case they will be .ft.json files):

https://github.com/JetBrains/intellij-community/tree/master/java/java-impl/src/fileTemplates/code

(you'll need to have the same folder structure in your plugin, i.e., your-plugin/src/fileTemplates/code)

For an example on how to use them, check this:

https://github.com/JetBrains/intellij-community/blob/master/java/java-impl/src/com/intellij/psi/impl/JavaPsiImplementationHelperImpl.java

(setupCatchBlock method).

 

You'll need to use JsonElementGenerator to create JSON tree elements, or just put the text generated via a template to your new file.

0
Comment actions Permalink

Hello Anton, 

That is okay.

thank you very much for your insights. okay, so in this case, I can not put it beside the other file templates that I put under resource folder? (picture attached)

And after the creation, if I want to use JSONElementGenretaor, it will be the same as 
```
public abstract PsiClass createClass(@NotNull PsiDirectory dir, @NotNull String name, @NotNull String templateName, boolean askForUndefinedVariables)
```
Does that get the template as input? 

thanks for your help

0
Comment actions Permalink

Yes, resources/fileTemplates should work as well.

You pointed in a correct direction yourself, you should take a look at com.intellij.psi.impl.file.JavaDirectoryServiceImpl#createClassFromTemplate.

You can either adopt the CreateFromTemplateDialog dialog (which is language-agnostic), or update your own dialog to perform a similar activity of creating a file from the template.

In this case you don't need to use JSONElementGenerator (you'll need it only if creating new tree elements).

0
Comment actions Permalink

Sorry to disturb you again, but as I know the aforementioned functions the main usage is for creating Java classes. so, if I passed the JSON template it doesn't rais an error in this case? or it will be fine?

0
Comment actions Permalink

You shouldn't use JavaDirectoryServiceImpl#createClassFromTemplate directly, you should create your own logic similar to this, with your own template and with some json-specific code.

CreateFromTemplateDialog is language-agnostic, so it's fine to use it.

0

Please sign in to leave a comment.