Jackson YAML parsing with parent objects & array of objects - java

I have a yaml file with four parent objects. The fourth object is an array of element of which I will want to create a class for and populate. Is there a way to use the object mapper in jackson to ignore the first three objects and then parse my list of "InspectionModules"?
My yaml file looks like this:
# Don't care about these elements
InspectionGroups:
InspecitonModes:
ThresholdTypes:
# This array is of interest
InspectionModules:
- feature : FC_Trig1
name: "first inspection"
Channels:
- id: ICI_01
category: Dia_MC_Config
- feature : FC_Trig2
name: "Diagonal Missing Cap"
Channels:
- id: ICI_02
category: Dia_MC_Config
Basically I want to create a class called InspectionModule and have the mapper map the elements of the InspectionModules array into this class. Is there a simple way to do this in jackson? If not would it be recommended to reorganize our YAML file so we can leverage the object mapper?

I assume you already have some familiarity with Jackson's ObjectMapper.
First you will need a Java class representing the entire
contents of your YAML file. Let's call it Root:
public class Root {
#JsonProperty("InspectionModules")
private List<InspectionModule> inspectionModules;
// getters and setters omitted here for brevity
}
Notice that you'll need to use #JsonProperty to tell Jackson
that the YAML name InspectionModules corresponds to your
Java property inspectionModules in spite of their different
spellings.
Next you need a Java class representing one of the
YAML sections below the InspectionModules:.
public class InspectionModule {
private String feature;
private String name;
#JsonProperty("Channels")
private List<Channel> channels;
// getters and setters omitted here for brevity
}
And finally you need a Channel class representing one of the
YAML sections below Channels:
You should be able to write this Java class by yourself already.
Now you are ready to use Jackson's YAMLMapper for reading your YAML
file into a Root object.
File file = new File("example.yaml");
ObjectMapper objectMapper = new YAMLMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Root root = objectMapper.readValue(file, Root.class);
Notice that you need to tell Jackson that it is OK to encounter
unknown properties (like InspectionGroups) during reading.

Related

How to change json field names in bulk

I have some json in a hashmap<string, object> and i’m using ObjectMapper to map it json. I need to change the field names of pretty much all of the values.
ex:
change this
{ “field1”:”abc”, “field2”: “xyz”}
to this
{“f1”:”abc”, “f2”:”xyz”}
I’ve read about using Jackson and using annotation #JsonProperty, but in my case it’s just not feasible because i need to change the names of atleast 20+ fields. What’s the best way to do this?
Why can’t you change ur inout source? Such large change is unnecessary and taxing
If you are parsing this Json with ObjectMapper, after getting intermediate DTO you can apply Mixin and export into String and then convert to required DTO.
Convert String to OriginalDTO with field1
Set Jackson mixin to object mapper
ObjectMapper mapper = new ObjectMapper();
#Getter
#Setter
public abstract class FinalMixin {
#JsonProperty("f1")
private String field1;
}
mapper.addMixInAnnotations(OriginalDTO.class, FinalMixin.class);
Convert DTO that you get in step 1 into String
Convert result String into FinalDTO

Problem with Jackson (JSON) parsing Map with ArrayList inside using ObjectMapper and PolymorphicTypeValidator without tags

I have read many posts about JSON parsing but still trying to resolve errors passing a java.util.HashMap that contains a class with a java.util.ArrayList inside it. I am ultimately passing this to JBPM. I have been trying to avoid using tags to avoid adding another dependency in Business Central (although perhaps Business Central might need this information too for JSON parsing.
More specifically, I have the project with a POJO and 2 classes:
public class AmsUsers {
private List<AmsUser> peoplePayload;
...
public class AmsUser {
private String id;
private String firstName;
private String lastName;
...
This project is a transitive dependency to my client in my project called RESTProject that makes REST calls to JBPM. JBPM imports the object model project. When I call my REST client via a JUnit which uses:
private static ObjectMapper mapper = new ObjectMapper();
...
PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder().build();
mapper.activateDefaultTyping(ptv, ObjectMapper.DefaultTyping.NON_CONCRETE_AND_ARRAYS);
URIBuilder ub = new URIBuilder(url);
AmsUser u1 = new AmsUser("my id"....);
AmsUser u2 = new AmsUser("second id"....);
ArrayList<AmsUser> names = new ArrayList<AmsUser>();
names.add(u1);
names.add(u2);
AmsUsers users = new AmsUsers();
users.setPeoplePayload(names);
hash.put(MAP_USERS_ASSIGNING_KEY, users);
String hashMapString = mapper.writeValueAsString(hash);
I get the following logged:
handling map of parameters for {
"people":["com.goprecise.ams.AmsUsers",{"peoplePayload":["java.util.ArrayList",[
{"id":"AA8E0125-EED9-4682-0000-5BAA665FC2F7","firstName":"D",.... },
{"id":"AA8E0125-EED9-4682-0001-5BAA665FC2F7","firstName":"David", ... }
]]}]}
could not parse map Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.util.Map
How do I get the classes in ArrayList passed to indicate that it is ArrayList of AmsUser's without annotations or custom code? Do I need to just accept annotating the classes (which would add dependencies in Business Central but could help it do its JSON parsing as well)? If so, what annotations should I use? Thanks!

jackson serialize csv property order

we have a table with 350+ columns. pojo class is generated and getters order get messed up. trying to use csvmapper from jackson, but it generates csv based on getter order. #JsonPropertyOrder is also not use feasible because of many columns.we maintain column ordering in xml and can generate field order array at runtime. can we override at runtime to provide array of fieldnames for property ordering? can we customize using annotation introspector?
What you are looking for is called a MappingFeature. You need to disable alphanumeric sorting of properties, which is enabled by default:
CsvMapper mapper = new CsvMapper();
mapper.disable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY);
More on this you can find here: Add a feature in CsvSchema to allow definition of ordering #42
Just in case you get here in 2020, this comes straight from the documentation.
So how do you get a CSV Schema instance to use? There are 3 ways:
Create schema based on a Java class
Build schema manually.
Use the first line of CSV document to get the names (no types) for Schema Here
is code for above cases:
// Schema from POJO (usually has #JsonPropertyOrder annotation)
CsvSchema schema = mapper.schemaFor(Pojo.class);
// Manually-built schema: one with type, others default to "STRING"
CsvSchema schema = CsvSchema.builder()
.addColumn("firstName")
.addColumn("lastName")
.addColumn("age", CsvSchema.ColumnType.NUMBER)
.build();
// Read schema from the first line; start with bootstrap instance
// to enable reading of schema from the first line
// NOTE: reads schema and uses it for binding
CsvSchema bootstrapSchema = CsvSchema.emptySchema().withHeader();
ObjectMapper mapper = new CsvMapper();
mapper.readerFor(Pojo.class).with(bootstrapSchema).readValue(json);
Note that #JsonPropertyOrder does not necessarily have to include all properties, just ones you are to include for serialization. But to indicate what is to be serialized you may need to use combination of #JsonProperty (to indicate properties to serialize) and different visibility for inclusion (either via ObjectMapper.setVisibility() for defaults, or via #JsonAutoDetect for per-POJO).
But assuming you do not want to use #JsonPropertyOrder, you can:
Override method in JacksonAnnotationIntrospector that reads the annotation, provide your own implementation that uses other sources (does not need to come from annotations at all)
If using Jackson 2.8.0, there is new way to specify per-class defaults for some things (see ObjectMapper.configOverride() object), including property order
Similarly you could override method that looks for #JsonProperty (findNameForDeserialization() and/or findNameForSerialization()) if you want to use custom criteria for inclusion/exclusion.
There are other mechanisms for inclusion/exclusion as well, like JSON Views (#JsonView), JSON Filters.
I believe your only choice here is uniVocity-parsers, as it allows you to choose which columns to write and in what order:
CsvWriterSettings settings = new CsvWriterSettings();
// Sets the file headers (used for selection only, these values won't be written automatically)
settings.setHeaders("Year", "Make", "Model", "Description", "Price");
// Selects which fields from the input should be written. In this case, fields "make" and "model" will be empty
// The field selection is not case sensitive
settings.selectFields("description", "price", "year");
//configures the writer process java beans with annotations (assume TestBean has a few annotated fiedls)
settings.setRowWriterProcessor(new BeanWriterProcessor<TestBean>(TestBean.class));
// Creates a writer with the above settings;
CsvWriter writer = new CsvWriter(new File("/path/to/output.csv"), settings);
// Writes the headers specified in the settings
writer.writeHeaders();
//creates a bean instance for writing
TestBean bean = new TestBean();
bean.setPrice(new BigDecimal("500.33"));
bean.setDescription("Blah,blah");
bean.setYear(1997);
//writes it
writer.processRecord(bean);
writer.close();
Hope it helps.
Disclosure: I'm the author of this libary, it's open-source and free (Apache 2.0 License)

How to omit a tag with Xstream?

I have a class like this:
public class revision{
long number;
String comment;
... getters and setters
}
and I want the xstream result to give be like this:
<revision comment="value of comment">
121556
</revision>
However, since number is field it forces me to write it in a <number> tag.
I used this to build the xml:
XStream xstream = new XStream(new DomDriver("UTF-8"));
xstream.useAttributeFor(Revision.class, "comment");
is there a possibility to not show the tag?
You need to register a ToAttributedValueConverter for the revision class. This converter lets you specify a single property of the class that should be mapped to the character content of the element, and all other properties are mapped to attributes on the element. The simplest way to do this is with annotations:
#XStreamConverter(value=ToAttributedValueConverter.class, strings={"number"})
public class revision {
// class body as before
}
and tell the XStream instance to read the annotations with
xstream.processAnnotations(revision.class);
With this converter in place you don't need the useAttributeFor call, as the converter will automatically use attributes for everything except the number.
If you don't want to use annotations then you can configure the converter with a method call on the XStream instance instead, you just have to extract the various helpers from xstream and pass them to the constructor explicitly (the annotation processor passes these things to the converter automatically if it requires them)
xstream.registerConverter(new ToAttributedValueConverter(
revision.class, xstream.getMapper(), xstream.getReflectionProvider(),
xstream.getConverterLookup(), "number"));

Jackson parse json with unwraping root, but without ability to set #JsonRootName

The rest service responds with
<transaction><trxNumber>1243654</trxNumber><type>INVOICE</type></transaction>
or in JSON:
{"transaction":{"trxNumber":1243654,"type":"INVOICE"}}
There is no problems when I use:
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true)
And as resulting class
#JsonRootName("transaction")
public class Transaction {
private String trxNumber;
private String type;
//getters and setters
}
But actually I should use the Transaction class from 3rd party jar, which is exact like above, but has no #JsonRootName("transaction") annotation.
So I end up with
Could not read JSON: Root name 'transaction' does not match expected ('Transaction') for type...
Is there any ways to force Jackson parse to Transaction class without adding any stuff to the Transaction class itself (as I get this file as part of a binary jar)?
I've tried custom PropertyNamingStrategy, but it seems has to do only with field and getter/setter names, but not class names.
Java7, Jackson 2.0.5.
Any suggestions? thanks.
You can do it with mixin feature. You can create simple interface/abstract class like this:
#JsonRootName("transaction")
interface TransactionMixIn {
}
Now, you have to configure ObjectMapper object:
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
mapper.addMixInAnnotations(Transaction.class, TransactionMixIn.class);
And finally you can use it to deserialize JSON:
mapper.readValue(json, Transaction.class);
Second option - you can write custom deserializer for Transaction class.

Categories

Resources