10

Spannedusingに変換している HTML がいくつかあり、その中でHtml.fromHtml(...)使用しているカスタム タグがあります。

<customtag id="1234">

TagHandlerしたがって、次のように、このカスタム タグを処理するために を実装しました。

public void handleTag( boolean opening, String tag, Editable output, XMLReader xmlReader ) {

    if ( tag.equalsIgnoreCase( "customtag" ) ) {

        String id = xmlReader.getProperty( "id" ).toString();
    }
}

この場合、「id」フィールドは実際にはプロパティではなく属性であると思われるため、SAX 例外が発生します。ただし、のgetAttribute()方法はありませんXMLReader。私の質問は、これを使用して「id」フィールドの値を取得するにはどうすればよいXMLReaderですか? ありがとう。

4

5 に答える 5

9

xmlReaderこれは、リフレクションによってのプライベート属性を取得するための私のコードです。

Field elementField = xmlReader.getClass().getDeclaredField("theNewElement");
elementField.setAccessible(true);
Object element = elementField.get(xmlReader);
Field attsField = element.getClass().getDeclaredField("theAtts");
attsField.setAccessible(true);
Object atts = attsField.get(element);
Field dataField = atts.getClass().getDeclaredField("data");
dataField.setAccessible(true);
String[] data = (String[])dataField.get(atts);
Field lengthField = atts.getClass().getDeclaredField("length");
lengthField.setAccessible(true);
int len = (Integer)lengthField.get(atts);

String myAttributeA = null;
String myAttributeB = null;

for(int i = 0; i < len; i++) {
    if("attrA".equals(data[i * 5 + 1])) {
        myAttributeA = data[i * 5 + 4];
    } else if("attrB".equals(data[i * 5 + 1])) {
        myAttributeB = data[i * 5 + 4];
    }
}

値をマップに入れることもできますが、私の使用法ではオーバーヘッドが大きすぎることに注意してください。

于 2013-03-04T07:08:25.000 に答える
8

XmlReaderprovided byを使用して、リフレクションなしでタグ属性値にアクセスすることは可能ですTagHandlerが、その方法はリフレクションよりもさらに簡単ではありません。ContentHandlerトリックは、 used byXmlReaderをカスタム オブジェクトに置き換えることです。置換ContentHandlerは への呼び出しでのみ実行できますhandleTag()。これにより、最初のタグの属性値を取得する際に問題が発生します。これは、html の先頭にカスタム タグを追加することで解決できます。

import android.text.Editable;
import android.text.Html;
import android.text.Spanned;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import java.util.ArrayDeque;

public class HtmlParser implements Html.TagHandler, ContentHandler
{
    public interface TagHandler
    {
        boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes);
    }

    public static Spanned buildSpannedText(String html, TagHandler handler)
    {
        // add a tag at the start that is not handled by default,
        // allowing custom tag handler to replace xmlReader contentHandler
        return Html.fromHtml("<inject/>" + html, null, new HtmlParser(handler));
    }

    public static String getValue(Attributes attributes, String name)
    {
        for (int i = 0, n = attributes.getLength(); i < n; i++)
        {
            if (name.equals(attributes.getLocalName(i)))
                return attributes.getValue(i);
        }
        return null;
    }

    private final TagHandler handler;
    private ContentHandler wrapped;
    private Editable text;
    private ArrayDeque<Boolean> tagStatus = new ArrayDeque<>();

    private HtmlParser(TagHandler handler)
    {
        this.handler = handler;
    }

    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader)
    {
        if (wrapped == null)
        {
            // record result object
            text = output;

            // record current content handler
            wrapped = xmlReader.getContentHandler();

            // replace content handler with our own that forwards to calls to original when needed
            xmlReader.setContentHandler(this);

            // handle endElement() callback for <inject/> tag
            tagStatus.addLast(Boolean.FALSE);
        }
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes)
            throws SAXException
    {
        boolean isHandled = handler.handleTag(true, localName, text, attributes);
        tagStatus.addLast(isHandled);
        if (!isHandled)
            wrapped.startElement(uri, localName, qName, attributes);
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException
    {
        if (!tagStatus.removeLast())
            wrapped.endElement(uri, localName, qName);
        handler.handleTag(false, localName, text, null);
    }

    @Override
    public void setDocumentLocator(Locator locator)
    {
        wrapped.setDocumentLocator(locator);
    }

    @Override
    public void startDocument() throws SAXException
    {
        wrapped.startDocument();
    }

    @Override
    public void endDocument() throws SAXException
    {
        wrapped.endDocument();
    }

    @Override
    public void startPrefixMapping(String prefix, String uri) throws SAXException
    {
        wrapped.startPrefixMapping(prefix, uri);
    }

    @Override
    public void endPrefixMapping(String prefix) throws SAXException
    {
        wrapped.endPrefixMapping(prefix);
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException
    {
        wrapped.characters(ch, start, length);
    }

    @Override
    public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
    {
        wrapped.ignorableWhitespace(ch, start, length);
    }

    @Override
    public void processingInstruction(String target, String data) throws SAXException
    {
        wrapped.processingInstruction(target, data);
    }

    @Override
    public void skippedEntity(String name) throws SAXException
    {
        wrapped.skippedEntity(name);
    }
}

このクラスを使用すると、属性を簡単に読み取ることができます。

    HtmlParser.buildSpannedText("<x id=1 value=a>test<x id=2 value=b>", new HtmlParser.TagHandler()
    {
        @Override
        public boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes)
        {
            if (opening && tag.equals("x"))
            {
                String id = HtmlParser.getValue(attributes, "id");
                String value = HtmlParser.getValue(attributes, "value");
            }
            return false;
        }
    });

このアプローチには、一部のタグの処理を無効にし、他のタグのデフォルト処理を使用できるという利点があります。たとえば、ImageSpanオブジェクトが作成されないようにすることができます。

    Spanned result = HtmlParser.buildSpannedText("<b><img src=nothing>test</b><img src=zilch>",
            new HtmlParser.TagHandler()
            {
                @Override
                public boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes)
                {
                    // return true here to indicate that this tag was handled and
                    // should not be processed further
                    return tag.equals("img");
                }
            });
于 2016-04-10T09:39:20.200 に答える
0

必要な属性が 1 つだけの場合、vorrtex による提案は実際にはかなり堅実です。処理がいかに簡単かを示す例として、以下をご覧ください。

<xml>Click on <user1>Johnni<user1> or <user2>Jenny<user2> to see...</<xml>

カスタム TagHandler では equals を使用せず、indexOf を使用します

final static String USER = "user";
if(tag.indexOf(USER) == 0) {
    // Extract tag postfix.
    String postfix = tag.substring(USER.length());
    Log.d(TAG, "postfix: " + postfix);
}

そして、onClick ビュー パラメータで接尾辞の値をタグとして渡して、汎用性を維持することができます。

于 2014-07-02T11:35:02.013 に答える