Hooli Hooli - 1 year ago 207
Java Question

com.fasterxml.jackson.core.JsonGenerationException: Unrecognized column 'name':

I've been trying to serialize an object to a CSV

String
but the object contains a
List
and
@JsonUnwrapped
doesn't work on
List
objects.

Expected sample output:

color,part.name\n
red,gearbox\n
red,door\n
red,bumper


Here is my code: (Most of it is the 2 POJO's)

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.dataformat.csv.CsvMapper;
import com.fasterxml.jackson.dataformat.csv.CsvSchema;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import java.io.IOException;
import static java.util.Arrays.asList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class NestedWrapping {

@JsonRootName("Car")
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonPropertyOrder({"color"})
public static class Car {

@JsonProperty("color")
private String color;

@JsonFormat(shape = JsonFormat.Shape.STRING)
@JacksonXmlElementWrapper(useWrapping = false)
private List<Part> parts;

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

public List<Part> getParts() {
return parts;
}

public void setParts(List<Part> parts) {
this.parts = parts;
}

}

@JsonInclude(JsonInclude.Include.NON_DEFAULT)
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
@JsonPropertyOrder({
"name"
})
public static class Part {

@JsonProperty("name")
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

public static void main(String args[]) {
try {
Car car = new Car();
car.setColor("red");
Part part1 = new Part();
part1.setName("geabox");
Part part2 = new Part();
part2.setName("door");
Part part3 = new Part();
part3.setName("bumper");
car.setParts(asList(part1, part2, part3));
System.out.println("serialized: " + serialize(car, Car.class, true));
} catch (IOException ex) {
Logger.getLogger(NestedWrapping.class.getName()).log(Level.SEVERE, null, ex);
}
}

public static final synchronized String serialize(final Object object, final Class type, final Boolean withHeaders) throws IOException {
CsvMapper csvMapper = new CsvMapper();
CsvSchema csvSchema;
if (withHeaders) {
csvSchema = csvMapper.schemaFor(type).withHeader();
} else {
csvSchema = csvMapper.schemaFor(type).withoutHeader();
}
return csvMapper.writer(csvSchema).writeValueAsString(object);
}

}


Nothing I try seems to work, I've read every post on stackoverflow and github about the topic but I can't find a working solution.

Sorry about any pointless annotations that I've left behind for no reason and if you answer with code, please feel free to remove them.

Answer Source

From the error, I would like to believe that it has something to do with your schema for a Car, which has the columns of {"color"} taken from @JsonPropertyOrder on Car and not a "name" value.

You probably want to add "parts" in there, but you would get the same error that "name" is not part of that schema.

After a few changes to your code, I was able to serialize and deserialize a Car object.

Part

Here, after some other changes it needed a constructor with a single String value, so add that

@JsonPropertyOrder({"name"})
public static class Part {
    @JsonProperty("name")
    private String name;

    public Part() {
        this("");
    }

    public Part(String name) {
        this.name = name;
    }

Car

Here, you will need to implement a method that will convert the List<Part> into a CSV-readable format manually.

Such a method would look like this

@JsonGetter("parts")
public String getPartString() {
    String separator = ";";
    StringBuilder sb = new StringBuilder();

    Iterator<Part> iter = this.parts.iterator();
    while (iter.hasNext()) {
        Part p = iter.next();
        sb.append(p.getName());

        if (iter.hasNext())
            sb.append(separator);
    }

    return sb.toString();
} 

Also, don't forget to fix the schema at the top of the class

@JsonPropertyOrder({"color", "parts"})
public static class Car {

    @JsonProperty("color")
    private String color;
    @JsonProperty("parts")
    private List<Part> parts;

    public Car() {
        this.parts = new ArrayList<>();
    }

serialize

You can change your serialize method to take the type of the class as a generic type parameter instead of an explicit Class like so.

public static final synchronized <T> String serialize(final T object, final Boolean withHeaders) throws IOException {
    CsvMapper csvMapper = new CsvMapper();
    CsvSchema csvSchema = csvMapper.schemaFor(object.getClass());

    if (withHeaders) {
        csvSchema = csvSchema.withHeader();
    } else {
        csvSchema = csvSchema.withoutHeader();
    }

    return csvMapper.writer(csvSchema).writeValueAsString(object);
}

main - writer

Now, if you serialize a Car, you should see

color,parts
red,gearbox;door;bumper

main - reader

And reading that CSV string and looping over the Car.getParts()

Car car = mapper.readerFor(Car.class).with(csvSchema).readValue(csv);

for (Part p : car.getParts()) {
    System.out.println(p.getName());
}
gearbox
door
bumper