1

必要な要素をシリアル化するときに「空の要素タグ」を回避する正しい方法は何ですか?

例:

@ElementList(name="rpc", required=true)
public ArrayList<FunctionRequest> getRequestedFunctions() {
    return requestedFunctions;
}

空のリストは、参照される「空の要素タグ」スタイルの単一のXML要素になります。

<rpc/>

私が実際に必要としているのは、次の表現です。

<rpc></rpc>

編集:さまざまなタイプのリストのソリューションが必要です!たとえば、例では「rpc」である注釈の、、、、...PLUS異なる名前属性List<String>List<int>存在する可能性があります。List<WhateverClass>

ご協力いただきありがとうございます。

ソリューション

最初に、実際に実装し、プロジェクトで使用しているソリューションを紹介します。その後、Simpleコードに直接追加できる変更の提案を提示します。

私が実際に使用しているソリューション:

XML要素を手動で定義して、プログラムにあるリストごとにConverterを作成したくなかったので、クラスの既存のアノテーションと、アノテーションの名前属性を使用することにしました。私のソリューションは、ゲッターとセッターを使用する場合にシリアル化されるクラスで機能します。現在、アノテーションAttributeTextアノテーションのみを使用するクラスにバインドされています。

より柔軟にするために、「基本」クラスを作成しましたRequestListConverter。2つのprotected方法prepareMethodListwriteRequestprepareMethodListリフレクションを使用して特定のクラスのすべてのメソッドを調べ、method-annotation-mapを作成します。writeRequest次に、メソッドに指定されたタイプの単一のオブジェクトを、インターフェイスのメソッドでprepareMethodList指定されたSimpleのOutputNodeに書き込みます。writeConverter

public class RequestListConverter {
    private HashMap<Method, Object> curMethodAnnotationMap = new HashMap<Method, Object>(); 

    @SuppressWarnings("rawtypes")
    protected void prepareMethodList(Class targetClass) {
        /*
         * First, get the annotation information from the given class.
         * 
         * Since we use getters and setters, look for the "get" methods!
         */

        Method[] curMethods = targetClass.getMethods();
        for (Method curMethod : curMethods) {
            String curName = curMethod.getName();

            // We only want getter methods that return a String
            if (curName.startsWith("get") && (curMethod.getReturnType() == String.class)) {
                Attribute curAttrAnnotation = curMethod.getAnnotation(Attribute.class);
                Text curTextAnnotation = curMethod.getAnnotation(Text.class);

                if (curAttrAnnotation != null) {
                    curMethodAnnotationMap.put(curMethod, curAttrAnnotation);
                } else
                if (curTextAnnotation != null) {
                    curMethodAnnotationMap.put(curMethod, curTextAnnotation);
                }
            }
        }
    }

    protected void writeRequest(OutputNode curNode, Object curRequest) throws Exception {
        for (Map.Entry<Method, Object> curMapEntry : curMethodAnnotationMap
                .entrySet()) {
            if ((curMapEntry.getKey() == null)
                    || (curMapEntry.getValue() == null)) {
                continue;
            }

            Method curMethod = curMapEntry.getKey();

            Attribute curAttrAnnotation = null;
            Text curTextAnnotation = null;

            if (curMapEntry.getValue() instanceof Attribute) {
                curAttrAnnotation = (Attribute) curMapEntry.getValue();
            } else if (curMapEntry.getValue() instanceof Text) {
                curTextAnnotation = (Text) curMapEntry.getValue();
            } else {
                continue;
            }

            String curValue = null;
            try {
                // Try to invoke the getter
                curValue = (String) curMethod.invoke(curRequest);
            } catch (IllegalAccessException | IllegalArgumentException
                    | InvocationTargetException e) {

                // The getter method seems to need any argument, strange! Skip
                // this!
                continue;
            }

            // If the method has an Attribute annotation, then ...
            if (curAttrAnnotation != null) {
                boolean curAttrRequired = curAttrAnnotation.required();
                String curAttrName = curAttrAnnotation.name();

                /*
                 * IF the returned method value is NULL THEN IF if the attribute
                 * is required THEN throw a NullPointerException, ELSE skip the
                 * attribute
                 */
                if (curValue == null) {
                    if (curAttrRequired) {
                        throw new NullPointerException(
                                "The required attribute " + curAttrName
                                        + " returned NULL!");
                    } else {
                        continue;
                    }
                }

                // The attribute will be added as XML text now
                curNode.setAttribute(curAttrName, curValue);
            } else
            // If the method has a Text annotation, then ...
            if (curTextAnnotation != null) {
                // we only need to store it for later string creation
                curNode.setValue(curValue);
            }
        }

        curNode.commit();
    }
}

このクラスに基づいて、たとえば、クラスを作成しましたSetRequestListConverter。SimpleのConverterインターフェースを実装しているため、read実装されていないメソッドとwrite、要素を含む可能性のあるリストまたは空の可能性のあるリストを取得するメソッドを提供します。

この例は、クラスのConverterの実装を示していますSetRequestList。以前に導入された基本RequestConverterクラスを拡張し、Converter型付きをに実装しSetRequestListます。

public class SetRequestListConverter extends RequestListConverter implements Converter<SetRequestList> {

    @Override
    public SetRequestList read(InputNode newNode) throws Exception {
        return null;
    }

    @Override
    public void write(OutputNode newNode, SetRequestList newValue) throws Exception {
        if (newValue.requests.isEmpty()) {
            newNode.setValue("");
            return;
        }

        this.prepareMethodList(SetRequest.class);

        /*
         * Now we can go through all SetRequests and call the methods
         * to build the XML attributes and to get the element value (i.e. parameters) 
         */

        for (SetRequest curRequest : newValue.requests) {
            OutputNode curNode = newNode.getChild("set");

            this.writeRequest(curNode, curRequest);
        }
    }
}

使用されるのSetRequestListは、を保持する単純なクラスですArrayList<SetRequest>。これは、これが実際にはArrayListであるという事実を隠すために必要です。

@Root
@Convert(SetRequestListConverter.class)
public abstract class SetRequestList {
    protected ArrayList<SetRequest> requests = new ArrayList<SetRequest>();

    public void add(T newRequest) {
        requests.add(newRequest);
    }
}

このクラスは、次のように使用できます。

public class ClassToSerialize {
    private SetRequestList requestedSets = new SetRequestList();

    @Element(name="get", required=true)
    public SetRequestList getRequestedSets() {
        return requestedSets;
    }

    @Element(name="get", required=true)
    public void setRequestedSets(SetRequestList newRequestedSets) {
        requestedSets = newRequestedSets;
    }
}

要素を含む生成されたXMLは次のSetRequestListようになります。

<get>
    <set someAttribute="text" anotherAttribute="bla">Some Text</set>
    ...
</get>

SetRequestList空の生成されたXMLは次のようになります。

<get></get>

ええ、まさに私が必要としていたものに加えて、他のSetRequestクラスやその他のクラスで注釈を使用できるという事実もあります。XML構造を再度(再)定義する必要はありません!

Simpleのコード提案

注意:これは解決策の提案であり、テストされていません。

Simpleのソースコードを調べたところ、Formatterクラスが実際に開始タグと終了タグ、および空の要素タグを書き込んでいることがわかりました。オブジェクトを渡すことで作成されFormatます。SimpleのJavadocは、Formatクラスを次のように説明しています。

Formatオブジェクトは、生成されたXMLドキュメントの構造に関する情報を提供するために使用されます。

したがって、空の要素タグを作成する必要があるかどうかについての情報で拡張できます。このために、プライベート変数useEmptyEndTagと適切なgetterメソッドとsetterメソッドを追加しました。変数はtrueコンストラクター内で初期化されます。Format空の終了タグスタイルが作成すべきものでない場合は、オブジェクトの作成後にを使用して設定できますmyFormat.setUseEmptyEndTag(false)

クラスは、適切なコード位置で設定されたパラメーターにアクセスできるように、指定されたオブジェクトをFormatter保持する新しいプライベート変数によって拡張されます。Format空の終了タグは。内に書き込まれwriteEndます。公式のソースコードを見て、元のコードを確認してください。空の要素タグを避けるための私の提案は次のとおりです。

public void writeEnd(String name, String prefix) throws Exception {
    String text = indenter.pop();

    // This will act like the element contains text
    if ((last == Tag.START) && (!format.isUseEmptyEndTag())) {
        write('>');
        last = Tag.TEXT;
    }

    if (last == Tag.START) {
        write('/');
        write('>');
    } else {
        if (last != Tag.TEXT) {
            write(text);
        }
        if (last != Tag.START) {
            write('<');
            write('/');
            write(name, prefix);
            write('>');
        }
    }
    last = Tag.END;
}

この提案は、私の目にはより良い解決策であり、Simpleのソースコードに追加されることを願っています。

4

1 に答える 1

0

barakyが以前に書いたように<rpc></rpc>、ここに投稿されているようなConverterを使用して、タグを使用してパーツを解決できます。ElementListUnionに空のElementListの空のタグが含まれないようにします。この特別な部分がどこで行われるか
についてのコメントがあります。ExampleConverter

さらに、name属性を取得するには、ここを参照してください:Simpleを使用してカスタムコンバーターからフィールド注釈にアクセスするにはどうすればよいですか?

したがって、必要なのはConverterクラスの実装です。タイプ(など)についてintは、-classStringを確認してください。Transformer

于 2013-02-19T13:00:24.997 に答える