XML parsing with XMLTextDecodeAsDictionary

Why this block not present in web component

XMLTextDecodeAsDictionary

image

How can i get the same result of this block?

You need it?I can make an extension for this as well :wink:.Because i’ve made an extension before for jsonTextDecodeWithDictionaries:

Yes it will be a great help for people need parsing complex XML like XMLTV files
because i could extract the data i want with this block in app inventor but i found list view missing many features their so i have to complete the application in Kodular

1 Like

here you are :wink:
That should does as the app inventor block exactly.
AIX: io.mohamed.XmlTextDecodeAsDictionaries.aix (7 KB)

i maybe update the extension with something like that in the future.

5 Likes

Thank you @Mohamed_Tamer for your help :smile:

1 Like

when i use your extension it give this error

I will test it again when i have my mobile.it should work :thinking:Strange
Can you test it as apk, and tell me what happens?

it also give the same error
Capture7

i try it in app inventor and it is work fine
i do not know why it does not work in kodular

1 Like

It looks like the xmlParser class is not supported in kodular :cold_sweat:I will try to update it now :wink:

@Mohamed_Tamer the method should be something like this

import com.google.appinventor.components.runtime.Web.*;
.
.
.
@SimpleFunction(description = "Parse XML")
public Object XMLTextDecodeAsDictionary(String XmlText){
  return XMLTextDecodeAsDictionary(XmlText);
}

AI2 Component: com.google.appinventor.components.runtime.Web Class Reference

That won’t work because it only call the xmlTextDecodeasdictionary block, which is not available in kodular as well.What i’m trying to do now, is to add the xmlparser class with the extension so the extension can take the source from .

Hmmm… But this is App Inventor Library, not Kodular’s.

Then what about your JsonDecodeAsDictionary extension?

Kodular compiles the app inventor sources,that what makes the imports we add working with kodualr as well.But as kodular now compiling an old version of the sources from the app inventor, there isn’t a class called XmlParse in kodular yet.So the resolution fails.because there isn’t such file.But as app inventor using a new version from the app inventor sources the extension works there.

But the suggestion I give is not XmlParser method. It is in the com.google.appinventor.components.runtime.Web class, which might be possible because one of the staff here said they just forget to add that back in one of the topics.

Actually, this class

Will compile the latest version from the web component kodular have.Which doesn’t contain the xmlparseasdictionary block/method yet.So when you call this method in kodular it won’t be found.But, when kodular update the web source to the latest version, the method will be found, as in app inventor now.

Thank you @Mohamed_Tamer for clarification
Do kodular announce a specific date for this update?

I still can make it but it will be harder.I’m experimenting with it now :wink:

2 Likes

thank you for your effort

1 Like

Hello, I finally find a way to achieve your query.

Extension

com.watermelonice.XmlTextAsDictionary.aix (11.3 KB)

I tested, and the result seems to be same as the MIT AI2 Web component.
XMLParser code is from app inventor source.

Enjoy!

All code are here

Code
package com.watermelonice.XmlTextAsDictionary;

import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.*;

import com.google.appinventor.components.runtime.util.YailDictionary;
import com.google.appinventor.components.runtime.util.YailList;

import java.io.StringReader;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;


@DesignerComponent(version = 1,
                    category = ComponentCategory.EXTENSION,
                    description = "To decode XML text to dictionary.<br><br> By <a href=\"https://community.kodular.io/u/WatermelonIce\">WatermelonIce</a>",
                    nonVisible = true,
                    iconName = "aiwebres/extension.png")

@SimpleObject(external = true)

public class XmlTextAsDictionary extends AndroidNonvisibleComponent {
    
    public XmlTextAsDictionary(final ComponentContainer container) {
        super(container.$form());
    }

    @SimpleEvent(description = "Raises when error occurred.")
    public void ErrorOccurred(String error) {
        EventDispatcher.dispatchEvent(this, "ErrorOccurred", error);
    }
// Code from here is from app inventor source: Web.java
    @SimpleFunction(description = "XmlTextDecodeAsDictionary")
    public Object XMLTextDecodeAsDictionary(String xmlText) {
        try {
            XmlParser p = new XmlParser();
            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
            InputSource is = new InputSource(new StringReader(xmlText));
            is.setEncoding("UTF-8");
            parser.parse(is, p);
            return p.getRoot();
        } catch (final Exception e) {
            ErrorOccurred(e.getMessage());
            return new YailDictionary();
        }
    }

    
}

// Code down here is from app inventor sources: XmlParser.java
@SuppressWarnings("unchecked")
class XmlParser extends DefaultHandler {
  private static final String CONTENT_TAG = "$content";
  private YailDictionary root = null;
  private YailDictionary currentElement = null;
  private Deque<YailDictionary> stack = new LinkedList<>();

  @Override
  public void startElement(String uri, String localName, String qname, Attributes attributes) {
    YailDictionary el = new YailDictionary();
    el.put("$tag", qname);
    el.put("$namespaceUri", uri);
    el.put("$localName", localName.isEmpty() ? qname : localName);
    if (qname.contains(":")) {
      String[] parts = qname.split(":");
      el.put("$namespace", parts[0]);
    } else {
      el.put("$namespace", "");
    }
    YailDictionary attrs = new YailDictionary();
    for (int i = 0; i < attributes.getLength(); i++) {
      attrs.put(attributes.getQName(i), attributes.getValue(i));
    }
    el.put("$attributes", attrs);
    el.put(CONTENT_TAG, new ArrayList<>());
    if (currentElement != null) {
      ((List<Object>) currentElement.get(CONTENT_TAG)).add(el);
      if (!currentElement.containsKey(qname)) {
        currentElement.put(qname, new ArrayList<>());
      }
      ((List<Object>) currentElement.get(qname)).add(el);
      stack.push(currentElement);
    } else {
      root = el;
    }
    currentElement = el;
  }

  @Override
  public void characters(char[] ch, int start, int length) {
    List<Object> items = (List<Object>) currentElement.get(CONTENT_TAG);
    if (items instanceof ArrayList) {
      String content = new String(ch, start, length);
      content = content.trim();
      if (!content.isEmpty()) {
        items.add(content);
      }
    }
  }

  @Override
  public void endElement(String uri, String localName, String qname) {
    for (Entry<Object, Object> e : currentElement.entrySet()) {
      if (e.getValue() instanceof ArrayList) {
        e.setValue(YailList.makeList((List<?>) e.getValue()));
      }
    }
    if (!stack.isEmpty()) {
      currentElement = stack.pop();
    }
  }

  public YailDictionary getRoot() {
    return root;
  }
}
2 Likes

Great @WatermelonIce :+1:actually that whati’ve made exactly .Only I’ve added the XML parser in another file . But i had a small problem.So I
'v not completed it.
.S : I’ve rked it as solution as my extension wasn’t working properly

2 Likes