Tickaroo / tikxml

Modern XML Parser for Android
Apache License 2.0
423 stars 44 forks source link

Type Converter for a different type of a List for example RealmList<MyObject> #28

Open FabianTerhorst opened 8 years ago

FabianTerhorst commented 8 years ago

It's not possible right now, because RealmList<MyObject>.class doesn't work. The addTypeAdapter should support adding a typeadapter for a Java.lang.refl.Type.

FabianTerhorst commented 8 years ago

This is also needed for the support of a Map for example.

FabianTerhorst commented 8 years ago

With moshi for example you can use something like this Type realmIntegerList = Types.newParameterizedType(RealmInteger.class, RealmList.class);

FabianTerhorst commented 8 years ago

And for maps moshi use Type[] keyAndValue = Types.mapKeyAndValueTypes(type, rawType);

sockeqwe commented 8 years ago

Agreed!

Fabian Terhorst notifications@github.com schrieb am Do., 2. Juni 2016, 20:16:

And for maps moshi use Type[] keyAndValue = Types.mapKeyAndValueTypes(type, rawType);

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/Tickaroo/tikxml/issues/28#issuecomment-223359206, or mute the thread https://github.com/notifications/unsubscribe/AAjnrmc_4n92kTuULUtbzphV8qpO1wYqks5qHw_QgaJpZM4IslHC .

meierjan commented 8 years ago

Hi, currently trying to use TikXml for RealmList's. Any known workarounds?

sockeqwe commented 8 years ago

It's on my TO DO list, but has low priority ... I'm working right now on other things towards an 1.0 release, but will try to fix this before releasing 1.0 ...

A "workaround" (which for me is a much cleaner approach anyway) is to have XML annotated models and realm models in 2 separated classes.

meierjan commented 8 years ago

I don't think having 2 different entities for the same thing in the data layer is always a cleaner approach (especially if there already is a mapping between data and domain layer) but thanks for the hint.

Too bad this is a actually really nice library (I have always missed an annotation based xml-parsing library since ig-json-parser). What a pitty

btw the TypeConverter api of LoganSquare is not as nice as moshi's but having the JsonParser in parse(JsonParser parser) (equivalent to read(String string)) can be pretty powerful.

sockeqwe commented 8 years ago

Ok, so latest 0.5.5-SNAPSHOT adds pretty the same API as Moshi has (Also a class Types to create such generic parameterized types). You can now register TypeAdapter and TypeConerter by hand via Type instead of Class<T>.

So things likeType[] keyAndValue = Types.mapKeyAndValueTypes(type, rawType); or RealmList should work now, but you have to write manually a TypeAdapter and register that one.

I don't think having 2 different entities for the same thing in the data layer is always a cleaner approach (especially if there already is a mapping between data and domain layer) but thanks for the hint.

Let's talk about that again in one year after a small API change in one of your xml feeds crashes your app because of a missing Realm migration. But I agree, there is no always or recommendation that works for all kind of situations.

FabianTerhorst commented 8 years ago

Great feature. 👍 . Awesome work.

meierjan commented 8 years ago

Wow, that's the low priority speed? Really awesome work!

FabianTerhorst commented 8 years ago

One question, how can i prevent the annotation processor to generate a wrong ChildElementBinder that is using new ArrayList?

FabianTerhorst commented 8 years ago

So that the annotation processor is using the getTypeAdapter(type) and not getTypeAdapter(class)

FabianTerhorst commented 8 years ago

if (value.getObjects() == null) { value.setObjects(new ArrayList()); }

is the problem

FabianTerhorst commented 8 years ago

apt { arguments { " "//this can´t accept stuff like io.realm.RealmList } }

sockeqwe commented 8 years ago

Indeed, list are a special edge case. I'm rewriting the code generator right now and will consider that ...

FabianTerhorst commented 8 years ago

ok thanks.

meierjan commented 8 years ago

@FabianTerhorst could you manage to parse them? I failed.... (even in 0.8.1-SNAPSHOT)

sockeqwe commented 8 years ago

its not implemented properly for lists. this will be fixed with a future snapshot release

meierjan commented 7 years ago

Any ETA? (rly just looking forward to test this)

sockeqwe commented 7 years ago

No ETA :( I'm working on this parser while gradle is building ... But I don't think that this will be implemented until the end of February.

If you want to submit a pull request for this, I can give you some advises how and where to implement this.

Jan Meier notifications@github.com schrieb am Mi., 25. Jan. 2017, 12:36:

Any ETA?

— You are receiving this because you modified the open/close state.

Reply to this email directly, view it on GitHub https://github.com/Tickaroo/tikxml/issues/28#issuecomment-275086125, or mute the thread https://github.com/notifications/unsubscribe-auth/AAjnrlMHRPyTxsbuXdgG3z9k6QpC9hTwks5rVzOegaJpZM4IslHC .

meierjan commented 7 years ago

Yeah, please. A short introduction would be great.

AleSpero commented 6 years ago

Hello, i'm trying to use tikxml in conjunction with RealmList, but i'm not sure how to set up the type adapter. Can anyone give me a hand? Thank you :)

sockeqwe commented 6 years ago

I'm not on my laptop right now and I'm not sure if it works, but this could work:

class RealmTypeAdapter extends TypeAdapter<RealmList<Foo>>{

  @Override
  public RealmList<Foo> fromXml(XmlReader reader, TikXmlConfig config) {
      // you need to parse some peace a little bit manually
      // i.e. each element in the xml stream like <foo bar="123" /> 
      RealmList<Foo> fooList= new RealmList<>();
      while(reader.hasElements()) {
          reader.beginElement();
          String elementName = reader.nextElementName();
          TypeAapter<Foo> fooParser = config. getTypeAdapter(Foo.class);
          Foo foo = fooParser. fromXml(reader, config);
          fooList.add(foo);

       // not sure if this one is needed:
      reader.endElement();
      }
   }

  @Override
  public void toXml(XmlWriter writer, TikXmlConfig config, RealmList<Foo> value, String overridingXmlElementTagName)
      throws IOException {
      // TODO implement if needed
   }

}

Then when instantiating your TikXml instance do something like this:


TikXml tikXml = new TikXml.Builder()
         .addTypeAdapter(Types. newParameterizedType(RealmList.class, Foo.class))
         .build();

Maybe you also need a compileTimeChecks = false in your annotation like this: (not sure if required, try it out):

@Xml
class SomeData {
    @Element(compileTimeChecks = false)
     RealmList<Foo> fooList;
}
AleSpero commented 6 years ago

Thank you for the fast response, really appreciated. I'll look into it.

Another question: what if i have N objects which extend RealmObject (the objects are nothing special, they contain just a bunch of strings). Do i need to implement a typeadapter for all of them? or just for realmobject?

Right now if i have something like that:

    @Xml
    class SomeData {
        @Element
        Foo foo;
        //foo extends RealmObject
    }

It just work flawlessly. i feel like i can write a generic typeadapter for RealmLists without creating other typeadapters.

Here's what i got so far:

    public class RealmListTypeAdapter implements TypeAdapter<RealmList<? extends RealmObject>> {

    @Override
    public RealmList<? extends RealmObject> fromXml(XmlReader reader, TikXmlConfig config) throws IOException {

        RealmList<? extends RealmObject> list = new RealmList<RealmObject>();

        while(reader.hasElement()){

            reader.beginElement();
            reader.nextElementName();
            //and now?

        }

        return null;
    }

Thank you. :)

sockeqwe commented 6 years ago
public class RealmListTypeAdapter implements TypeAdapter<RealmList<? extends RealmObject>> {

    @Override
    public RealmList<? extends RealmObject> fromXml(XmlReader reader, TikXmlConfig config) throws IOException {

        RealmList<? extends RealmObject> list = new RealmList<RealmObject>();

        while(reader.hasElement()){

            reader.beginElement();
             String elementName =  reader.nextElementName();
            //and now?
            if (elementName.equals(foo)){
               TypeAapter<Foo> fooParser = config. getTypeAdapter(Foo.class);
               RealmObject foo = fooParser. fromXml(reader, config);
               list.add(foo);
            } else {
               TypeAapter<Bar> barParser = config. getTypeAdapter(Bar.class);
               RealmObject bar = fooParser. fromXml(reader, config);
               list.add(bar);
           }
        }

        return list;
    }

You basically have to make a mapping from "String" (ElementName) to the java class you would like to parse. You can use Map<String, Class>, then use the returned Class to get the corresponding TypeAdapter.

Perhaps there will be an easier way to do this in the future (like an abstract class your type adapter has to extend) or a better API i.e. an additional Annotation but I can't make any promise at this time ...

komuros commented 6 years ago

Hello, I use the solution you write for RealmList but I have an error : error: <identifier> expected value.previsions = config.getTypeAdapter(RealmList<Foo>.class).fromXml(reader, config);

Can you help me to resolve this error ?

CppGeekMajor commented 1 year ago

Hello, how and where is RealmList declared?