从 Java 以编程方式导入 XML:项目已导入但返回的列表为空

Importing XMLs programmatically from Java: items are imported but the returned list is empty

这是一个示例 Java 代码,它尝试使用 %SYSTEM.OBJ's LoadStream method 导入 XML 导出中可用的 类; import 指令已被省略 "brevity":

public final class Main2
{
    private static final String CACHEDB_HOST = "cachedb.host";
    private static final String CACHEDB_PORT = "cachedb.port";
    private static final String CACHEDB_USER = "cachedb.user";
    private static final String CACHEDB_PASSWORD = "cachedb.password";
    private static final String CACHEDB_NAMESPACE = "cachedb.namespace";
    private static final String LOADEDFILE = "loadedFile";

    private static final String CACHEDB_HOST_DEFAULT = "localhost";
    private static final String CACHEDB_PORT_DEFAULT = "1972";

    private static final String JDBC_URL_TEMPLATE = "jdbc:Cache://%s:%s/%s";

    private Main2()
    {
        throw new Error("instantiation not permitted");
    }

    public static void main(final String... args)
        throws IOException, CacheException
    {
        if (args.length == 0)
            throw new IllegalArgumentException("missing arguments");

        final Properties properties = new Properties();

        final Path path = Paths.get(args[0]).toRealPath();

        try (
            final Reader reader = Files.newBufferedReader(path);
        ) {
            properties.load(reader);
        }

        final String jdbcUrl = String.format(JDBC_URL_TEMPLATE,
            readProperty(properties, CACHEDB_HOST, CACHEDB_HOST_DEFAULT),
            readProperty(properties, CACHEDB_PORT, CACHEDB_PORT_DEFAULT),
            readProperty(properties, CACHEDB_NAMESPACE));

        final String user = readProperty(properties, CACHEDB_USER);
        final String password = readProperty(properties, CACHEDB_PASSWORD);

        final Path loadedFile = Paths.get(readProperty(properties, LOADEDFILE))
            .toRealPath();

        try (
            final CacheDb db = new CacheDb(jdbcUrl, user, password);
        ) {
            final GlobalCharacterStream stream
                = new GlobalCharacterStream(db.getDatabase());

            loadContent(stream, loadedFile);

            /*
             * Arguments for class "%SYSTEM.OBJ", class method "LoadStream"
             */
            final Dataholder[] arguments = new Dataholder[8];

            /*
             * Arguments ByRef
             *
             * Indices start at 1, not 0
             */
            final int[] byRefArgs = new int[2];

            // Arg 3: error log
            final StringHolder errorlog = new StringHolder("");
            byRefArgs[0] = 3;

            // Arg 4: list of loaded items
            final StringHolder loadedlist = new StringHolder("");
            byRefArgs[1] = 4;

            /*
             * Fill arguments
             */
            // arg 1: stream
            arguments[0] = Dataholder.create(stream);
            // arg 2: qspec; the default, therefore null
            arguments[1] = new Dataholder((String) null);
            // arg 3: errorlog
            arguments[2] = Dataholder.create(errorlog.value);
            // arg 4: loadedlist
            arguments[3] = Dataholder.create(loadedlist.value);
            // arg 5: listonly; we want true
            arguments[4] = Dataholder.create(Boolean.TRUE);
            // arg 6: selecteditems; nothing
            arguments[5] = Dataholder.create(null);
            // arg 7: displayname. For logging...
            arguments[6] = Dataholder.create("IMPORT");
            // arg 8: charset. Default is empty string, we'll assume UTF-8.
            arguments[7] = new Dataholder((String) null);

            // Now, make the call
            final Dataholder[] result = db.getDatabase().runClassMethod(
                "%SYSTEM.OBJ",
                "LoadStream",
                byRefArgs,
                arguments,
                Database.RET_PRIM
            );

            /*
             * The result normally has three members:
             *
             * - first is the status; and we need to do that:
             */
            db.getDatabase().parseStatus(result[0]);

            /*
             * - others are ByRef arguments
             */
            // FIXME: probably not ideal
            errorlog.set(result[1].getString());
            System.out.println("errorlog: " + errorlog.getValue());

            loadedlist.set(result[2].getString());
            System.out.println("loadedlist: " + loadedlist.getValue());
        }
    }

    private static void loadContent(final GlobalCharacterStream stream,
        final Path path)
        throws IOException, CacheException
    {
        final StringBuilder sb = new StringBuilder();

        try (
            final Reader reader = Files.newBufferedReader(path);
        ) {
            final char[] buf = new char[2048];
            int nrChars;

            while ((nrChars = reader.read(buf)) != -1)
                sb.append(buf, 0, nrChars);
        }

        stream._write(sb.toString());
    }

    private static String readProperty(final Properties properties,
        final String key)
    {
        final String ret = properties.getProperty(key);
        if (ret == null)
            throw new IllegalArgumentException("required property " + key
                + " is missing");
        return ret;
    }

    private static String readProperty(final Properties properties,
        final String key, final String defaultValue)
    {
        return properties.getProperty(key, defaultValue);
    }
}

现在,代码运行了;在 Studio 中,我看到项目也已导入。

然而输出是这样的(缓存安装是法语的,抱歉):

Inventaire démarré le 02/12/2016 11:16:38
Classement du fichier IMPORT en tant que xml
Inventaire terminé.

errorlog: 
loadedlist: null

我看不到导入了哪些项目。

我做错了什么?

Java 绑定支持获取 ByRef 值,你做对了。但不幸的是,有一个限制,您可以在这里找到它。使用 Caché 中的 ByRef 我们可以传递数组,如下所示

array("name1")="value1"
array("name2")="value2"

但是在Java中我们不能得到这样的值,只有当数组在"root"中有值时。 Load 方法同时,有将数组转换为值列表的代码,我们已经可以得到这样的值。因此,作为一种解决方法,我建议将 %GlobalCharacterStream 替换为 %FileCharacterStream,并使用一些扩展名为 xml 的临时文件名。然后我们可以在 Load 方法中使用这个文件名。因此,经过一些更改后,代码应如下所示:

    final FileCharacterStream stream = new FileCharacterStream(db);

    Dataholder[] args = new Dataholder[]{new Dataholder("xml")};
    Dataholder res = ((SysDatabase) db).runClassMethod("%File", "TempFilename", args, 0);
    stream._filenameSet(res.getString());

    loadContent(stream, path);

    final String remoteFileName = stream._filenameGet();


    /*
     * Arguments for class "%SYSTEM.OBJ", class method "Load"
     */
    final Dataholder[] arguments = new Dataholder[9];

    /*
     * Arguments ByRef
     *
     * Indices start at 1, not 0
     */
    final int[] byRefArgs = new int[3];

    // Arg 3: error log
    final StringHolder errorlog = new StringHolder("");
    byRefArgs[0] = 3;

    // Arg 4: list of loaded items
    final StringHolder loadedlist = new StringHolder("");
    byRefArgs[1] = 4;

    // Arg 9: description (?)
    final StringHolder description = new StringHolder("");
    byRefArgs[2] = 9;

    /*
     * Fill arguments
     */
    // arg 1: file name
    arguments[0] = Dataholder.create(remoteFileName);
    // arg 2: qspec; we want to ensure that compile works, at least
    arguments[1] = new Dataholder("d");
    // arg 3: errorlog
    arguments[2] = Dataholder.create(errorlog.value);
    // arg 4: loadedlist
    arguments[3] = Dataholder.create(loadedlist.value);
    // arg 5: listonly; no
    arguments[4] = Dataholder.create(Boolean.FALSE);
    // arg 6: selecteditems; nothing
    arguments[5] = Dataholder.create(null);
    // arg 7: displayname. For logging...
    arguments[6] = Dataholder.create("IMPORT.xml");
    // arg 8: charset. Default is empty string, we'll assume UTF-8.
    arguments[7] = new Dataholder((String) null);
    // arg 9: description (?)
    arguments[8] = Dataholder.create(description.value);

    // Now, make the call
    final Dataholder[] result = db.runClassMethod(
        "%SYSTEM.OBJ",
        "Load",
        byRefArgs,
        arguments,
        Database.RET_PRIM
    );

    /*
     * The result normally has three members:
     *
     * - first is the status; and we need to do that:
     */
    db.parseStatus(result[0]);

    /*
     * - others are ByRef arguments
     */
    errorlog.set(result[1].getString());
    System.out.println("errorlog: " + errorlog.getValue());

    loadedlist.set(result[2].getString());
    System.out.println("loadedlist: " + loadedlist.getValue());

结果

 Load started on 02/12/2016 22:56:06
 Loading file IMPORT.xml as xml
 Imported class: Sample.Address
 Imported class: Sample.Person
 Load finished successfully.

 errorlog: 
 loadedlist: Sample.Address.cls,Sample.Person.cls