如何从Java中的字符串创建对象(如何评估字符串)?

时间:2021-07-08 19:40:38

I know eval is "evil", but I'm using it in a way that the user can't ever abuse it.

我知道eval是“邪恶的”,但我正在以一种用户不能滥用它的方式使用它。

Let's say I've got a string "new Integer(5)". I want to do something such that I can set a variable, let's say foo, to new Integer(5). Something like

假设我有一个字符串“new Integer(5)”。我想做一些事情,我可以设置一个变量,比如说foo,给新的Integer(5)。就像是

Integer foo;
String bar = "new Integer(5)"
*magic happens*
System.out.println(foo) -> 5

I've looked around and it looks like I have a few options. Can the getSystemJavaCompiler() method in ToolProvider do this? Or should I use BeanShell? Or is there something else? Note that this is from a string, not a file.

我环顾四周,看起来我有几个选择。 ToolProvider中的getSystemJavaCompiler()方法可以这样做吗?或者我应该使用BeanShell?或者还有其他什么?请注意,这是来自字符串,而不是文件。

7 个解决方案

#1


I would use a scripting language like beanshell, jruby, jython, etc.

我会使用像beanshell,jruby,jython等脚本语言。

#2


You'd have to use something like Janino.

你必须使用像Janino这样的东西。

#3


Here's a possible way to get most of the way there via using javax.tools. Note that this code is rather long and not exactly the most efficient or portable way to do this, but you should get the idea.

这是通过使用javax.tools获得大部分方法的可能方法。请注意,此代码相当长,并不是最有效或最便携的方法,但您应该明白这一点。

import javax.tools.*;
import java.util.*;
import java.io.*;
import java.lang.reflect.*;

public class Test {
  public static void main(String[] args) throws Exception {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    JavaFileObject fileObj = 
      new StringJavaFileObject("public class InterpTest { public static void test() { System.out.println(\"Hello World\"); } }");
    List<JavaFileObject> tasks = new ArrayList<JavaFileObject>();
    tasks.add(fileObj);

    JavaFileManager defFileMgr = compiler.getStandardFileManager(null, null, null);
    MemoryJavaFileManager fileMgr = new MemoryJavaFileManager(defFileMgr);
    compiler.getTask(null, fileMgr, null, null, null, tasks).call();

    ClassLoader loader = new ByteArrayClassLoader();
    Class clazz = loader.loadClass("InterpTest");
    Method method = clazz.getMethod("test");
    method.invoke(null);
  }

  public static class StringJavaFileObject extends SimpleJavaFileObject {
    protected String str;

    public StringJavaFileObject(String str) {
      super(java.net.URI.create("file:///InterpTest.java"), JavaFileObject.Kind.SOURCE);
      this.str = str;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncErrors) {
      return str;
    }
  }

  public static class MemoryJavaFileObject extends SimpleJavaFileObject {
    public static ByteArrayOutputStream out = new ByteArrayOutputStream();

    public MemoryJavaFileObject(String uri, JavaFileObject.Kind kind) {
      super(java.net.URI.create(uri), kind);
    }

    @Override
    public OutputStream openOutputStream() {
      return out;
    }
  }

  public static class ByteArrayClassLoader extends ClassLoader {
    public Class findClass(String name) {
      byte[] bytes = MemoryJavaFileObject.out.toByteArray();
      return super.defineClass(name, bytes, 0, bytes.length);
    }
  }

  public static class MemoryJavaFileManager implements JavaFileManager {
    protected JavaFileManager parent;

    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
      return new MemoryJavaFileObject("file:///InterpTest.class", kind);
    }

    public MemoryJavaFileManager(JavaFileManager parent) { this.parent = parent; }
    public void close() throws IOException { parent.close(); }
    public void flush() throws IOException { parent.flush(); }
    public ClassLoader getClassLoader(JavaFileManager.Location location) { return parent.getClassLoader(location); }
    public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relName) throws IOException { return parent.getFileForInput(location, packageName, relName); }
    public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relName, FileObject sibling) throws IOException { return parent.getFileForOutput(location, packageName, relName, sibling); }
    public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException { return parent.getJavaFileForInput(location, className, kind); }
    public boolean handleOption(String current, Iterator<String> remaining) { return parent.handleOption(current, remaining); }
    public boolean hasLocation(JavaFileManager.Location location) { return parent.hasLocation(location); }
    public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) { return parent.inferBinaryName(location, file); }
    public boolean isSameFile(FileObject a, FileObject b) { return parent.isSameFile(a, b); }
    public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { return parent.list(location, packageName, kinds, recurse); }
    public int isSupportedOption(String option) { return parent.isSupportedOption(option); }
  }
}

#4


This kind of thing is possible, but it would be horrendously expensive for a task as simple as this. In this example, I'd consider using Class.forName() to map "Integer" to a class, and Java reflection invoke the Constructor.

这种事情是可能的,但对于像这样简单的任务来说,这将是非常昂贵的。在这个例子中,我考虑使用Class.forName()将“Integer”映射到一个类,Java反射调用Constructor。

#5


The Integer class takes a String in its constructor to set the value, assuming the provided string contains only numeric text.

假设提供的字符串仅包含数字文本,Integer类在其构造函数中采用String来设置值。

Integer foo;

public void setFoo(String str) {
   if(isInt(str)) {
     foo = new Integer(str.trim());
   }
}

// Returns a boolean based on if the provided string contains only numbers
private boolean isInt(String str) {
  boolean isInt = true;

  try {
    Integer.parseInt(str.trim());
  } catch (NumberFormatException nfe) {
    isInt = false;
  }

  return isInt;
}

// Get the value as an int rather than Integer
public int getIntValue(String str) {
  return Integer.parseInt(str.trim());
}

#6


Java is a statically typed language, so I don't think you can do that.

Java是一种静态类型语言,所以我认为你不能这样做。

#7


You can use Java Scripting API. Default language is JavaScript, but you can plug in any language. it would require java 1.6 though.

您可以使用Java Scripting API。默认语言是JavaScript,但您可以插入任何语言。但它需要java 1.6。

#1


I would use a scripting language like beanshell, jruby, jython, etc.

我会使用像beanshell,jruby,jython等脚本语言。

#2


You'd have to use something like Janino.

你必须使用像Janino这样的东西。

#3


Here's a possible way to get most of the way there via using javax.tools. Note that this code is rather long and not exactly the most efficient or portable way to do this, but you should get the idea.

这是通过使用javax.tools获得大部分方法的可能方法。请注意,此代码相当长,并不是最有效或最便携的方法,但您应该明白这一点。

import javax.tools.*;
import java.util.*;
import java.io.*;
import java.lang.reflect.*;

public class Test {
  public static void main(String[] args) throws Exception {
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    JavaFileObject fileObj = 
      new StringJavaFileObject("public class InterpTest { public static void test() { System.out.println(\"Hello World\"); } }");
    List<JavaFileObject> tasks = new ArrayList<JavaFileObject>();
    tasks.add(fileObj);

    JavaFileManager defFileMgr = compiler.getStandardFileManager(null, null, null);
    MemoryJavaFileManager fileMgr = new MemoryJavaFileManager(defFileMgr);
    compiler.getTask(null, fileMgr, null, null, null, tasks).call();

    ClassLoader loader = new ByteArrayClassLoader();
    Class clazz = loader.loadClass("InterpTest");
    Method method = clazz.getMethod("test");
    method.invoke(null);
  }

  public static class StringJavaFileObject extends SimpleJavaFileObject {
    protected String str;

    public StringJavaFileObject(String str) {
      super(java.net.URI.create("file:///InterpTest.java"), JavaFileObject.Kind.SOURCE);
      this.str = str;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncErrors) {
      return str;
    }
  }

  public static class MemoryJavaFileObject extends SimpleJavaFileObject {
    public static ByteArrayOutputStream out = new ByteArrayOutputStream();

    public MemoryJavaFileObject(String uri, JavaFileObject.Kind kind) {
      super(java.net.URI.create(uri), kind);
    }

    @Override
    public OutputStream openOutputStream() {
      return out;
    }
  }

  public static class ByteArrayClassLoader extends ClassLoader {
    public Class findClass(String name) {
      byte[] bytes = MemoryJavaFileObject.out.toByteArray();
      return super.defineClass(name, bytes, 0, bytes.length);
    }
  }

  public static class MemoryJavaFileManager implements JavaFileManager {
    protected JavaFileManager parent;

    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
      return new MemoryJavaFileObject("file:///InterpTest.class", kind);
    }

    public MemoryJavaFileManager(JavaFileManager parent) { this.parent = parent; }
    public void close() throws IOException { parent.close(); }
    public void flush() throws IOException { parent.flush(); }
    public ClassLoader getClassLoader(JavaFileManager.Location location) { return parent.getClassLoader(location); }
    public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relName) throws IOException { return parent.getFileForInput(location, packageName, relName); }
    public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relName, FileObject sibling) throws IOException { return parent.getFileForOutput(location, packageName, relName, sibling); }
    public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException { return parent.getJavaFileForInput(location, className, kind); }
    public boolean handleOption(String current, Iterator<String> remaining) { return parent.handleOption(current, remaining); }
    public boolean hasLocation(JavaFileManager.Location location) { return parent.hasLocation(location); }
    public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) { return parent.inferBinaryName(location, file); }
    public boolean isSameFile(FileObject a, FileObject b) { return parent.isSameFile(a, b); }
    public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { return parent.list(location, packageName, kinds, recurse); }
    public int isSupportedOption(String option) { return parent.isSupportedOption(option); }
  }
}

#4


This kind of thing is possible, but it would be horrendously expensive for a task as simple as this. In this example, I'd consider using Class.forName() to map "Integer" to a class, and Java reflection invoke the Constructor.

这种事情是可能的,但对于像这样简单的任务来说,这将是非常昂贵的。在这个例子中,我考虑使用Class.forName()将“Integer”映射到一个类,Java反射调用Constructor。

#5


The Integer class takes a String in its constructor to set the value, assuming the provided string contains only numeric text.

假设提供的字符串仅包含数字文本,Integer类在其构造函数中采用String来设置值。

Integer foo;

public void setFoo(String str) {
   if(isInt(str)) {
     foo = new Integer(str.trim());
   }
}

// Returns a boolean based on if the provided string contains only numbers
private boolean isInt(String str) {
  boolean isInt = true;

  try {
    Integer.parseInt(str.trim());
  } catch (NumberFormatException nfe) {
    isInt = false;
  }

  return isInt;
}

// Get the value as an int rather than Integer
public int getIntValue(String str) {
  return Integer.parseInt(str.trim());
}

#6


Java is a statically typed language, so I don't think you can do that.

Java是一种静态类型语言,所以我认为你不能这样做。

#7


You can use Java Scripting API. Default language is JavaScript, but you can plug in any language. it would require java 1.6 though.

您可以使用Java Scripting API。默认语言是JavaScript,但您可以插入任何语言。但它需要java 1.6。