2

次のような XML ドキュメントがあります。

<!-- language: xml -->
<items>
  <item type="java.lang.Boolean" name="foo" value="true" />
</items>

<root>要素でオブジェクトを作成し、java.util.Map<item>要素で適切な型のオブジェクトを作成してから、エントリをMap--に追加しますSetNextRuleが、スタックからの呼び出しに引数を付けます。

属性の値を使用して、属性で指定されたタイプ (この場合)Ruleのオブジェクトを作成し、それをスタックにプッシュするカスタムを既に作成しました。typejava.lang.Booleanvalue

ここで、アイテムをスタックの一番上からポップし、それをオブジェクト (スタック上のオブジェクトの「すぐ下」 ) のputメソッドへの引数として使用したいと思います。MapBoolean

これまでに書いたコードは次のとおりです。

<!-- language: lang-java -->
Digester digester = new Digester();
digester.addObjectCreate("items", HashMap.class);
digester.addRule(new MyObjectCreateRule()); // This knows how to create e.g. java.lang.Boolean objects
digester.addCallMethod("items/item", "put", 2, new Class<?>[] { String.class, Object.class });
digester.addCallParam("items/item", 0, "name");
digester.addCallParam("items/item", 1, true); // take argument from stack

クラス内にメソッドputが見つからないというエラーが表示されjava.lang.Booleanます。したがって、問題は、egオブジェクトがスタックの一番上にあり、スタックの一番上から 2 番目の要素で呼び出されるメソッドBooleanへの引数として使用したいということです。put

スタック:

java.lang.Boolean value=true     <-- top of stack, desired call param
java.util.HashMap contents = {}  <-- desired call target

既存の commons-digester ルールでこれを行う方法はありますか? または、このタイプの操作を実行する別のカスタム ルールを作成する必要がありますか?

4

2 に答える 2

0

別のアプローチとして、ダイジェスター自体から問題を取り除き、拡張されたマップ クラスを使用して、既存のダイジェスター ルールとより互換性のあるメソッドを提供することができます。

public static class MyHashMap extends HashMap {
  public Object put(String clazz, String name, String value) {
    Object obj = ... // create object from clazz/name/value
    return super.put(name, obj);
  }
}

次に、既存のaddCallMethod/addCallParamルールを使用します。

Digester digester = new Digester();
digester.addObjectCreate("items", MyHashMap.class);
digester.addCallMethod("items/item", "put", 3, new Class<?>[] { String.class, String.class, String.class });
digester.addCallParam("items/item", 0, "type");
digester.addCallParam("items/item", 1, "name");
digester.addCallParam("items/item", 2, "value");

HashMapカスタム クラスではなく結果としてピュアを取得する必要がある場合はHashMap、たとえばcom.google.common.collect.ForwardingMapGuava を使用している場合など、ネイティブ クラスをラップするカスタム クラスで同様の方法を使用できます。

于 2017-12-10T23:17:46.747 に答える
0

最終的に、プロパティ値の新しいインスタンスを作成し、それをプロパティ バンドルに挿入するという 2 つの操作を組み合わせたカスタム ルールを作成しました。

これは私が持っていた実際のユースケースの適応であるため、ここでコピー/貼り付けして適応させたため、コードは 100% 完璧ではない可能性があります。また、以外のプロパティ値を使用することjava.lang.Stringはあまり意味がないことも理解していますが、私のユースケースではそうでした (java.util.Properties実際には を使用しませんが、そのクラスは良い類推でした)。

<!-- language: lang-java -->
/**
 * Implements a create-object-set-property Digester rule.
 */
public class SetPropertyRule
    extends Rule
{
    private String _classAttributeName;
    private String _nameAttributeName;
    private String _valueAttributeName;
    private HashSet<String> _acceptableClassNames;

    /**
     * Creates a new SetPreferenceRule with default attribute names and classes.
     *
     * Default class attribute name = "type".
     * Default name attribute name = "name".
     * Default value attribute name = "value".
     * Default allowed classes = String, Integer, Double, and Boolean.
     */
    public SetPropertiesRule()
    {
        this("type", "name", "value",
             new Class<?>[] { String.class, Integer.class, Double.class, Boolean.class });
    }

    /**
     * Creates a new SetPropertyRule to construct a name/value pair and
     * set it on a Properties object.
     *
     * The Properties object should be at the top of the current
     * Digester stack.
     *
     * @param classAttributeName The name of the attribute that holds the property's value type.
     * @param nameAttributeName The name of the attribute that holds the property's name.
     * @param valueAttributeName The name of the attribute that holds the property's value.
     * @param acceptableClasses The list of acceptable property value types.
     */
    public SetPreferenceRule(String classAttributeName, String nameAttributeName, String valueAttributeName, Class<?>[] acceptableClasses)
    {
        super();

        _classAttributeName = classAttributeName;
        _nameAttributeName = nameAttributeName;
        _valueAttributeName = valueAttributeName;
        _acceptableClassNames = new HashSet<String>(acceptableClasses.length);
        for(Class<?> clazz : acceptableClasses)
            _acceptableClassNames.add(clazz.getName());
    }

    @Override
    public void begin(String namespace,
                      String name,
                      Attributes attributes)
        throws Exception
    {
        // Store the values of these attributes on the digester param stack
        getDigester().pushParams(
                attributes.getValue(_classAttributeName),
                attributes.getValue(_nameAttributeName),
                attributes.getValue(_valueAttributeName)
        );
    }

    @Override
    public void end(String namespace,
                    String name)
        throws Exception
    {
        Object[] attributeValues = getDigester().popParams();

        Object props = getDigester().peek();
        if(!(props instanceof java.util.Properties))
        {
            String typeName;
            if(null == props)
                typeName = "<null>";
            else
                typeName = props.getClass().getName();

            throw new IllegalStateException("Expected instance of " + Properties.class.getName() + ", got " + typeName + " instead");
        }

        String className = (String)attributeValues[0];
        checkClassName(className);

        // Create an instance of the preference value class
        Class<?> clazz = Class.forName(className);
        Constructor<?> cons = clazz.getConstructor(String.class);
        Object value = cons.newInstance((String)attributeValues[2]);

        ((Properties)props).put((String)attributeValues[1], value);
    }

    private void checkClassName(String className)
    {
        if(!_acceptableClassNames.contains(className))
            throw new IllegalArgumentException("Class " + className + " is not allowed");
    }
}

ただし、これを行うためのすぐに使える方法があることを発見できれば幸いです。

于 2017-11-25T15:10:12.060 に答える