通过地图列表使用 Apache Camel 将 CSV 文件转换为 JSON 文件

Convert CSV file to JSON file with Apache Camel via List of Maps

目标是学习如何将 CSV 读取到 Maps 列表,然后如何将其编组到 JSON。

一旦我明白了如何去做,我就会知道如何定义更有用的路线。

我使用XML来定义路由,另一个限制是不创建任何转换bean,而是只使用现有的组件。

我的理解显然缺乏一些概念。我知道你必须提供一个bean作为消费者,然后你可以传递它;但是文档说 csv 数据格式使用的 Maps 列表有什么问题?

    <dataFormats>
        <json id="jack" library="Jackson"/>
    </dataFormats>  

    <route>
        <from uri="file:///C:/tries/collApp/exchange/in?fileName=registerSampleSmaller.csv"/>
        <unmarshal>
            <csv />
        </unmarshal>            
        <marshal ref="jack">                
        </marshal>
        <to uri="file:///C:/tries/collApp/exchange/out?fileName=out.json"/>          
    </route>

默默无闻。我只能看到锁定文件是如何出现和消失的。

谢谢!

ps/ 我期待着创建两条路线,第一条路线将读取 csv,对其进行转换 - 将其平坦的性质塑造成我的持久性 bean,然后将其传递给我的 bean。第二个只是将我的 beans 保存为 json,这似乎是一个简单的部分;但我首先需要这样做来了解它是如何工作的

请查看此数据格式页面[1] 您可以使用那些元帅 DSL 将对象转换为具有您想要的甲酸盐的字符串。 [1]http://camel.apache.org/data-format.html

为什么要地图列表?为什么不是列表?

  1. 使用 Bindy 将 CSV 解组为 POJO 列表。
  2. 使用带注释的 类 或标准杰克逊代码将列表编组到 JSON。

如果您需要代码示例,请告诉我。但主要思想是在解组 csv 后始终检查正文。您很可能会得到一份 pojo 列表。然后只需迭代列表并为每个 pojo 获取 "getters" 并设置 json 标签的值。

我在前进的过程中提供答案。

我在正确的轨道上,只是有一些小错误。 Jérémie B 在对原始问题的评论中注意到了一个。

它无声地失败了,因为我没有启用日志记录,我通过在 pom.xml:

中添加这样的 slf4j 来做到这一点
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>${slf4j-version}</version>
    </dependency>    
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-jdk14</artifactId>
        <version>${slf4j-version}</version>
    </dependency>    
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>jcl-over-slf4j</artifactId>
        <version>${slf4j-version}</version>
    </dependency>    

我看到了很多错误,甚至是 Camel 越野车行为,但我设法使这条路线可行:

    <dataFormats>
        <json id="jack" library="Jackson" prettyPrint="true"/>
    </dataFormats>       

    <route>

        <from uri="file:///C:/tries/collApp/exchange/in?fileName=registerSampleUtf.csv&amp;charset=UTF-8"/>
        <log message="file: ${body.class.name} ${body}" loggingLevel="WARN"/>
        <unmarshal>
            <csv delimiter=";"  useMaps="true" />
        </unmarshal>           
        <log message="unmarshalled: ${body.class.name} ${body}" loggingLevel="WARN"/>
        <marshal ref="jack"/>
        <log message="marshalled: ${body}" loggingLevel="WARN"/>
        <to uri="file:///C:/tries/collApp/exchange/out?fileName=out.json"/>         
    </route>

所以基本上,在清理拼写错误后我不得不

  • 指定输入文件字符集,

  • 指定 Excel 用于创建我的 csv 的分隔符,

  • 告诉把它放在地图上。

不幸的是,这个特定的代码不起作用,可能是由于我向开发者社区报告的 Camel 错误(仍然没有反应,http://camel.465427.n5.nabble.com/A-possible-bug-in-IOConverter-with-Win-1251-charset-td5778665.html

虽然我向前迈进了一步,但现在我可能正在绕过有缺陷的 Camel 的 IOConverter,目前我正处于这个阶段(这不是对问题的回答,只是为了提供信息,Camel 有多方便):

    <route>
        <from uri="file:///C:/tries/collApp/exchange/in?fileName=registerSampleSmaller1.csv&amp;charset=windows-1251"/>
        <split streaming="true">
            <method ref="csvSplitter" method="tokenizeReader"/>  <!-- aprepends the first line of file for every subsequent line -->
            <log message="splitted: ${body}" loggingLevel="DEBUG"/>
            <unmarshal>
                <csv delimiter=";"  useMaps="true" />
            </unmarshal>            
            <log message="unmarshalled: size: ${body.size()}, ${body}" loggingLevel="DEBUG"/>
            <filter>
                <simple>${body.size()} == 1</simple><!-- be sure to have spaces around an operator -->
                <log message="filtered: listItem: ${body[0]['PATRONYMIC']}, list: ${body}" loggingLevel="DEBUG"/>
                <transform>
                    <spel>#{
                        {
                        lastName:body[0]['LAST_NAME'],
                        firstName: body[0]['FIRST_NAME'],
                        patronymic: body[0]['PATRONYMIC'],
                        comment:body[0]['COMMENT6']
                        }
                        }</spel><!-- split the spel {:} map creation notation in multiline is crucial-->
                </transform>                
                <log message="transformed: ${body}" loggingLevel="DEBUG"/>
                <marshal ref="jack"/>
                <log message="marshalled: ${body}" loggingLevel="DEBUG"/>
                <to uri="file:///C:/tries/collApp/exchange/out?fileName=out${exchangeProperty.CamelSplitIndex}.json"/>          
            </filter>
        </split>
    </route>

我必须编写自己的 CSV 拆分器(关于所有 Unicode 代码点等),这基本上是将第一行添加到所有后续行,但现在我能够将 CSV 拆分为一组 JSON流线型方式,或以不同方式处理对象而不是编组。

**更新 - csvSplitter 代码**

Reader Tokenizer - 围绕 reader:

的迭代器
public class ReaderTokenizer implements Iterator<String> {

private String _curString = null;
private boolean _endReached = false;
private final Reader _reader;
private char[] _token;

public ReaderTokenizer(Reader reader, String token) {
    setToken(token);
    _reader = reader;
}

public final void setToken(String token){
    _token = token.toCharArray();
    if(_token.length==0){
        throw new IllegalArgumentException("Can't tokenize with the empty string");
    }
}

private void _readNextToken() throws IOException {

    int curCharInt;
    char previousChar = (char) -1;
    int tokenPos = 0;
    StringBuilder sb = new StringBuilder(255);

    while (true) {
        curCharInt = _reader.read();
        if (curCharInt == -1) {
            _endReached = true;
            _reader.close();
            break;
        }
        if (curCharInt == _token[tokenPos]) {

            if (tokenPos != 0 || !Character.isHighSurrogate(previousChar)) {
                tokenPos++;

                if (tokenPos >= _token.length) {
                    tokenPos = 0;
                    previousChar = (char) curCharInt;
                    sb.append(previousChar);
                    break;
                }
            }
        }

        previousChar = (char) curCharInt;
        sb.append(previousChar);
    }
    _curString = sb.toString();
}

@Override
public boolean hasNext() {
    if (_curString == null) {
        if (_endReached) {
            return false;
        }
        try {
            _readNextToken();
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }

        if (_curString != null) {
            return true;
        }

        if (_endReached) {
            return false;
        }

        throw new RuntimeException("Someting wrong");

    } else {
        return true;
    }
}

@Override
public String next() {
    if (_curString != null) {
        String ret = _curString;
        _curString = null;
        return ret;
    }
    if (_endReached) {
        throw new NoSuchElementException();
    }

    try {
        _readNextToken();
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }

    if (_curString != null) {
        String ret = _curString;
        _curString = null;
        return ret;
    }

    throw new RuntimeException("Someting wrong");
}

@Override
public void remove() {
    throw new UnsupportedOperationException("Not supported.");
}

}

分离器本身:

public class CamelReaderSplitter {

private final String _token;
private final int _headerLinesNumber;

public CamelReaderSplitter(String token, int headerLinesNumber) {
    _token = token;
    _headerLinesNumber = headerLinesNumber;
}

public CamelReaderSplitter(String token) {
    _token = token;
    _headerLinesNumber = 1;
}

public CamelReaderSplitter(int headerLinesNumber) {
    _token = "\r\n";
    _headerLinesNumber = headerLinesNumber;
}

public CamelReaderSplitter() {
    _token = "\r\n";
    _headerLinesNumber = 1;
}

public Iterator<String> tokenizeReader(final Reader reader) throws IOException {

    Iterator<String> ret = new ReaderTokenizer(reader, _token) {

        private final String _firstLines;

        {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < _headerLinesNumber; i++) {
                if (super.hasNext()) {
                    sb.append(super.next());
                }
            }
            _firstLines = sb.toString();
        }

        @Override
        public String next() {
            return _firstLines + super.next();
        }

    };

    return ret;

}

}