How to implement conversion for properties

125 views
Skip to first unread message

tonyp...@gmail.com

unread,
Nov 12, 2017, 6:38:22 PM11/12/17
to SimpleFlatMapper
I just started using SimpleFlatMapper and have a situation where some of the properties I need to populate need special handling. In some cases, I need to construct an enum and in other cases, I need to lookup a value, etc. What is the easiest way to accomplish this?

Thanks in advance for your help!

-Tony

tonyp...@gmail.com

unread,
Nov 12, 2017, 7:54:33 PM11/12/17
to SimpleFlatMapper

tonyp...@gmail.com

unread,
Nov 12, 2017, 9:43:33 PM11/12/17
to SimpleFlatMapper
Still not obvious to me the simplest way to handle the conversion.  I have a property named engineType which is an enum (EngineType). What I read from the CSV is a code, in the enum I have a factory method (get) that can be used to get the instance that corresponds to the specified code. Here is the code I am writing to handle the mapping. Here is my code so far:

CsvMapper<Plane> mapper = 
    CsvMapperFactory.newInstance()
                    .addCustomValueReader("engineType", (chars, offset, length, ctx) -> {

                       return EngineType.get(String.valueOf(chars));

                    }).newBuilder(PlaneInfo.class)

                      .addMapping("id")

                      .addMapping("manufacturer")

                      .addMapping("model")

                      .addMapping("engineType")

                      .addMapping("year")

                      .mapper();

CsvParser.skip(1)

         .mapWith(mapper)

         .stream(source)

         .forEach(p -> System.out.println(p));


This seems to work but I would love to know if there is an easier way to do this.


Thanks in advance for your help!


-Tony


On Sunday, November 12, 2017 at 5:38:22 PM UTC-6, tonyp...@gmail.com wrote:

tonyp...@gmail.com

unread,
Nov 12, 2017, 10:45:50 PM11/12/17
to SimpleFlatMapper
One small correction to the mapper:

CsvMapper<Plane> mapper = 
    CsvMapperFactory.newInstance()
                    .addCustomValueReader("engineType", (charsoffsetlengthctx) -> {

                       return EngineType.get(String.valueOf(chars, offset, length));

                    }).newBuilder(PlaneInfo.class)

                      .addMapping("id")

                      .addMapping("manufacturer")

                      .addMapping("model")

                      .addMapping("engineType")

                      .addMapping("year")

                      .mapper();

Arnaud Roger

unread,
Nov 13, 2017, 10:36:42 AM11/13/17
to SimpleFlatMapper
The custom reader would be the easiest right now to solve that.
The converter is mainly useful for type where there is no way to get from the source and is using the ServiceLocator so not so easy to put in place. Also enum are a special cased and sfm has a direct path from String to enum so will never go through the converter.

If you don't want to manually specify the column name you could also create a dynamic mapper

CsvMapper<Plane> mapper =
    CsvMapperFactory.newInstance()
                      .addCustomValueReader("engineType", (charsoffsetlengthctx) -> EngineType.get(String.valueOf(chars, offset, length))

                      .newMapper(PlaneInfo.class);


CsvParser.mapWith(mapper)

         .stream(source)

         .forEach(p -> System.out.println(p));


avoiding all the addMapping. note that also the mapper is thread safe and if you will use it multiple time you can reuse the same over and over again

reducing the cost of mapper generation.


to make things simpler I might add a


addCustomValueReader(String key, Function<String, ?> reader) that would give you



CsvMapper<Plane> mapper =

    CsvMapperFactory.newInstance()
                      .addCustomValueReader("engineType", EngineType::get)

                      .newMapper(PlaneInfo.class);


that would be relatively straightforward


I'm sure if that help? Was the problem the builder plus the skip 1?

tonyp...@gmail.com

unread,
Nov 13, 2017, 11:00:49 AM11/13/17
to SimpleFlatMapper
I like your idea of adding that specific addCustomValueReader. It could be useful in some situations. What about adding a filter method on CsvParser that takes a predicate? That way you could easily ignore rows that are obviously bad. It would look something like the following:

CsvParser.skip(1)

         .filter(s -> s.length > 10)     // this would ignore lines that are too short

         .mapWith(mapper)

         .stream(source)

         .forEach(p -> System.out.println(p));


Please let me know your thoughts on this idea.

Thanks again!

-Tony

tonyp...@gmail.com

unread,
Nov 13, 2017, 11:11:49 AM11/13/17
to SimpleFlatMapper
Correction to the example code:

CsvParser.skip(1)

         .filter(s -> s.length() > 10)     // prev post was missing the parens after s.length

         .mapWith(mapper)

         .stream(source)

         .forEach(p -> System.out.println(p));


Reply all
Reply to author
Forward
0 new messages