I'm having tough time coming up with a class that represents the following JSON. "familyRelationships" is an array of arrays. Each array has person1 and person2 identifiers and the relationship between person1 and person2. As you might've already guessed, order of the values in array is very important. For example, if "12345" and "31142" positions switch, then it means "31142" is the PARENT of "12345" which is totally wrong.
{
"familyRelationships": [
[
"12345",
"SPOUSE",
"67890",
{
"careTaker": false,
"liveTogether": true
}
],
[
"12345",
"PARENT",
"31142",
{
"careTaker": true,
"liveTogether": true
}
],
[
"67890",
"PARENT",
"31142",
{
"careTaker": true,
"liveTogether": true
}
]
]
}
This should be doable using a custom deserializer.
A relationship in my opinion should be modeled as a proper java class with proper names. Note that the constructor takes a JSONNode as an argument and that I have left our any getters and setters:
public class Relationship {
private final String id1;
private final String id2;
private final Relation relation;
private final boolean careTaker;
private final boolean liveTogether;
public Relationship(JsonNode base) {
this.id1 = base.get(0).asText();
this.id2 = base.get(2).asText();
this.relation = Relation.valueOf(base.get(1).asText());
this.careTaker = base.get(3).get("careTaker").asBoolean();
this.liveTogether = base.get(3).get("liveTogether").asBoolean();
}
public enum Relation {
PARENT,
SPOUSE;
}
}
We also need a class which stores the collection. This is the one that you would deserialize the top level object into (again leaving out getters and setters):
#JsonDeserialize( using = FamillyRelationshipsDeserializer.class )
public class FamillyRelationships {
public List<Relationship> familyRelationships = new ArrayList<>();
}
Finally we need to implement the actual JsonDeserializer referenced in the above class. It should look something like the following. I used this question as a reference:
class FamillyRelationshipsDeserializer extends JsonDeserializer<FamillyRelationships> {
#Override
public FamillyRelationships deserialize(JsonParser jp, DeserializationContext ctxt) throws
IOException, JsonProcessingException {
FamillyRelationships relationships = new FamillyRelationships();
JsonNode node = jp.readValueAsTree();
JsonNode rels = node.get("familyRelationships");
for (int i = 0; i < rels.size(); i++) {
relationships.familyRelationships.add(new Relationship(rels.get(i));
}
return relationships;
}
}
I hope this helps, I haven't actually tested any of this, it probably will not even compile, but the principles should be right. I have also assumed that the JSON is of the format you supplied. If that's not a guarantee then you will need to make the proper checks and deal with any deviations.
If you also want to be able to serialize everything then you will need to implement JsonSerializer see this question for more details.
That JSON isn't going to nicely become a POJO because the types contained within the arrays are not consistent:
e.g.
[
"12345",
"SPOUSE",
"67890",
{
"careTaker": false,
"liveTogether": true
}
]
is String,String,String,Object. The only way that works is if you have a Object[] or List<Object>. So familyRelationships should actually be a List<List<Object>>. The end result is going to require a bunch of casting (and probably some checks to make sure that the item at a given index is the class you expect), but it will work.
There are some tools online to do that for you
package com.example;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"familyRelationships"
})
public class Example {
#JsonProperty("familyRelationships")
private List<List<String>> familyRelationships = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("familyRelationships")
public List<List<String>> getFamilyRelationships() {
return familyRelationships;
}
#JsonProperty("familyRelationships")
public void setFamilyRelationships(List<List<String>> familyRelationships) {
this.familyRelationships = familyRelationships;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
This is only 1 possiblilty made with http://www.jsonschema2pojo.org/
Related
I have below 2 Enums, One is for type and another is for value.
public enum DifferentiatorType {
#JsonProperty("differentiatorType")
MARKETPLACE_ID("MarketplaceId");
private final String value;
}
public enum DifferentiatorValue {
VALUE_ONE("valueOne");
VALUE_TWO("valueTwo");
VALUE_THREE("valueThree");
VALUE_FOUR("valueFour");
private final String value;
}
Now the situation is ,i have a payload which holds both the field DifferentiatorType and DifferentiatorValue. I want to check if the type belongs to DifferentiatorType then go and check if the value is present there in DifferentiatorValue then only proceed farther. So basically i want to create a map between the 2 Enums .
PS: In future new object/item can be added to DifferentiatorType and in that case we have to create new Enum for that type of values accepted.
Here is the sample payload:
{\"referenceId\":\"B01-2776421-8482453\",\"preferenceType\":\"CREDITCARD\",\"differentiatorValue\":\"ABXWY75J\",\"customerId\":\"A37I50ASYHT\",\"differentiatorType\":\"MarketplaceId\"}
Use EnumMap and EnumSet to hold all the relationships.
import java.util.Map;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Collection;
import java.util.Collections;
public class DifferentiatorMappings {
private final Map<DifferentiatorType, Collection<?>> values;
public DifferentiatorMappings() {
Map<DifferentiatorType, Collection<?>> map =
new EnumMap<>(DifferentiatorType.class);
map.put(DifferentiatorType.MARKETPLACE_ID,
Collections.unmodifiableCollection(
EnumSet.allOf(DifferentiatorValue.class)));
map.put(DifferentiatorType.ANOTHER_ID,
Collections.unmodifiableCollection(
EnumSet.allOf(AnotherValue.class)));
values = Collections.unmodifiableMap(map);
}
public Collection<?> getValuesFor(DifferentiatorType type) {
if (type == null) {
return Collections.emptySet();
}
return values.getOrDefault(type, Collections.emptySet());
}
}
Then you can use the returned values collection to check for a correct value:
DifferentiatorType type = payload.getDifferentiatorType();
Object value = payload.getDifferentiatorValue();
DifferentiatorMappings mappings = new DifferentiatorMappings();
if (mappings.getValuesFor(type).contains(value)) {
// proceed
}
I need to serialize and send several equal objects but with different key names which depend on amount number of them
{
"object1": {
"name": "random",
"pass": true
},
"object2": {
"name": "random",
"pass": false
},
"object3": {
"name": "random",
"pass": true
}
}
I use Lombok #Builder + Jackson #JsonProperty for body models describing to be sent but have no ideas how to handle the case when the same object might be added several times with numeric increasing key name without code duplication as at an example below.
#Builder
public class Sample {
#JsonProperty("object1")
private RandomObject object1;
#JsonProperty("object2")
private RandomObject object2;
#JsonProperty("object3")
private RandomObject object3;
}
You can do this,
public class Sample {
Map<String, RandomObject> objects = new HashMap<>();
public void add(String key, RandomObject val) {
objects.put(key, val);
}
#JsonAnyGetter
public Map<String, RandomObject> getObjects() {
return objects;
}
}
You can crate the response like this,
RandomObject object1 = RandomObject.builder().name("John").pass(true).build();
RandomObject object2 = RandomObject.builder().name("Jane").pass(false).build();
Sample sample = new Sample();
sample.add("object1", object1);
sample.add("object2", object2);
return sample;
Please also note that you can skip creating this Simple object and crate Map and return it as your response.
Is there a mechanism to apply a standard set of checks to detect and then transform a String to the detected type, using one of Jackson's standard text related libs (csv, json, or even jackson-core)? I can imagine using it along with a label associated with that value (CSV header, for example) to do something sorta like the following:
JavaTypeAndValue typeAndValue = StringToJavaType.fromValue(Object x, String label);
typeAndValue.type() // FQN of Java type, maybe
typeAndValue.label() // where label might be a column header value, for example
typeAndValue.value() // returns Object of typeAndValue.type()
A set of 'extractors' would be required to apply the transform, and the consumer of the class would have to be aware of the 'ambiguity' of the 'Object' return type, but still capable of consuming and using the information, given its purpose.
The example I'm currently thinking about involves constructing SQL DDL or DML, like a CREATE Table statement using the information from a List derived from evaluating a row from a csv file.
After more digging, hoping to find something out there, I wrote the start of what I had in mind.
Please keep in mind that my intention here isn't to present something 'complete', as I'm sure there are several things missing here, edge cases not addressed, etc.
The pasrse(List<Map<String, String>> rows, List<String> headers comes from the idea that this could be a sample of rows from a CSV file read in from Jackson, for example.
Again, this isn't complete, so I'm not looking to pick at everything that's wrong with the following. The question isn't 'how would we write this?', it's 'is anyone familiar with something that exists that does something like the following?'.
import gms.labs.cassandra.sandbox.extractors.Extractor;
import gms.labs.cassandra.sandbox.extractors.Extractors;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
#Accessors(fluent=true, chain=true)
public class TypeAndValue
{
#Builder
TypeAndValue(Class<?> type, String rawValue){
this.type = type;
this.rawValue = rawValue;
label = "NONE";
}
#Getter
final Class<?> type;
#Getter
final String rawValue;
#Setter
#Getter
String label;
public Object value(){
return Extractors.extractorFor(this).value(rawValue);
}
static final String DEFAULT_LABEL = "NONE";
}
A simple parser, where the parse came from a context where I have a List<Map<String,String>> from a CSVReader.
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.math.NumberUtils;
import java.util.*;
import java.util.function.BiFunction;
public class JavaTypeParser
{
public static final List<TypeAndValue> parse(List<Map<String, String>> rows, List<String> headers)
{
List<TypeAndValue> typesAndVals = new ArrayList<TypeAndValue>();
for (Map<String, String> row : rows) {
for (String header : headers) {
String val = row.get(header);
TypeAndValue typeAndValue =
// isNull, isBoolean, isNumber
isNull(val).orElse(isBoolean(val).orElse(isNumber(val).orElse(_typeAndValue.apply(String.class, val).get())));
typesAndVals.add(typeAndValue.label(header));
}
}
}
public static Optional<TypeAndValue> isNumber(String val)
{
if (!NumberUtils.isCreatable(val)) {
return Optional.empty();
} else {
return _typeAndValue.apply(NumberUtils.createNumber(val).getClass(), val);
}
}
public static Optional<TypeAndValue> isBoolean(String val)
{
boolean bool = (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"));
if (bool) {
return _typeAndValue.apply(Boolean.class, val);
} else {
return Optional.empty();
}
}
public static Optional<TypeAndValue> isNull(String val){
if(Objects.isNull(val) || val.equals("null")){
return _typeAndValue.apply(ObjectUtils.Null.class,val);
}
else{
return Optional.empty();
}
}
static final BiFunction<Class<?>, String, Optional<TypeAndValue>> _typeAndValue = (type, value) -> Optional.of(
TypeAndValue.builder().type(type).rawValue(value).build());
}
Extractors. Just an example of how the 'extractors' for the values (contained in strings) might be registered somewhere for lookup. They could be referenced any number of other ways, too.
import gms.labs.cassandra.sandbox.TypeAndValue;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.math.NumberUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.List;
public class Extractors
{
private static final List<Class> NUMS = Arrays.asList(
BigInteger.class,
BigDecimal.class,
Long.class,
Integer.class,
Double.class,
Float.class);
public static final Extractor<?> extractorFor(TypeAndValue typeAndValue)
{
if (NUMS.contains(typeAndValue.type())) {
return (Extractor<Number>) value -> NumberUtils.createNumber(value);
} else if(typeAndValue.type().equals(Boolean.class)) {
return (Extractor<Boolean>) value -> Boolean.valueOf(value);
} else if(typeAndValue.type().equals(ObjectUtils.Null.class)) {
return (Extractor<ObjectUtils.Null>) value -> null; // should we just return the raw value. some frameworks coerce to null.
} else if(typeAndValue.type().equals(String.class)) {
return (Extractor<String>) value -> typeAndValue.rawValue(); // just return the raw value. some frameworks coerce to null.
}
else{
throw new RuntimeException("unsupported");
}
}
}
I ran this from within the JavaTypeParser class, for reference.
public static void main(String[] args)
{
Optional<TypeAndValue> num = isNumber("-1230980980980980980980980980980988009808989080989809890808098292");
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass()); // BigInteger
});
num = isNumber("-123098098097987");
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass()); // Long
});
num = isNumber("-123098.098097987"); // Double
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass());
});
num = isNumber("-123009809890898.0980979098098908080987"); // BigDecimal
num.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass());
});
Optional<TypeAndValue> bool = isBoolean("FaLse");
bool.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
System.out.println(typeAndVal.value().getClass()); // Boolean
});
Optional<TypeAndValue> nulll = isNull("null");
nulll.ifPresent(typeAndVal -> {
System.out.println(typeAndVal.value());
//System.out.println(typeAndVal.value().getClass()); would throw null pointer exception
System.out.println(typeAndVal.type()); // ObjectUtils.Null (from apache commons lang3)
});
}
I don't know of any library to do this, and never seen anything working in this way on an open set of possible types.
For closed set of types (you know all the possible output types) the easier way would be to have the class FQN written in the string (from your description I didn't get if you are in control of the written string).
The complete FQN, or an alias to it.
Otherwise I think there is no escape to not write all the checks.
Furthermore it will be very delicate as I'm thinking of edge use case.
Suppose you use json as serialization format in the string, how would you differentiate between a String value like Hello World and a Date written in some ISO format (eg. 2020-09-22). To do it you would need to introduce some priority in the checks you do (first try to check if it is a date using some regex, if not go with the next and the simple string one be the last one)
What if you have two objects:
String name;
String surname;
}
class Employee {
String name;
String surname;
Integer salary
}
And you receive a serialization value of the second type, but with a null salary (null or the property missing completely).
How can you tell the difference between a set or a list?
I don't know if what you intended is so dynamic, or you already know all the possible deserializable types, maybe some more details in the question can help.
UPDATE
Just saw the code, now it seems more clear.
If you know all the possible output, that is the way.
The only changes I would do, would be to ease the increase of types you want to manage abstracting the extraction process.
To do this I think a small change should be done, like:
interface Extractor {
Boolean match(String value);
Object extract(String value);
}
Then you can define an extractor per type:
class NumberExtractor implements Extractor<T> {
public Boolean match(String val) {
return NumberUtils.isCreatable(val);
}
public Object extract(String value) {
return NumberUtils.createNumber(value);
}
}
class StringExtractor implements Extractor {
public Boolean match(String s) {
return true; //<-- catch all
}
public Object extract(String value) {
return value;
}
}
And then register and automatize the checks:
public class JavaTypeParser {
private static final List<Extractor> EXTRACTORS = List.of(
new NullExtractor(),
new BooleanExtractor(),
new NumberExtractor(),
new StringExtractor()
)
public static final List<TypeAndValue> parse(List<Map<String, String>> rows, List<String> headers) {
List<TypeAndValue> typesAndVals = new ArrayList<TypeAndValue>();
for (Map<String, String> row : rows) {
for (String header : headers) {
String val = row.get(header);
typesAndVals.add(extract(header, val));
}
}
}
public static final TypeAndValue extract(String header, String value) {
for (Extractor<?> e : EXTRACTOR) {
if (e.match(value) {
Object v = extractor.extract(value);
return TypeAndValue.builder()
.label(header)
.value(v) //<-- you can put the real value here, and remove the type field
.build()
}
}
throw new IllegalStateException("Can't find an extractor for: " + header + " | " + value);
}
To parse CSV I would suggest https://commons.apache.org/proper/commons-csv as CSV parsing can incur in nasty issues.
What you actually trying to do is to write a parser. You translate a fragment into a parse tree. The parse tree captures the type as well as the value. For hierarchical types like arrays and objects, each tree node contains child nodes.
One of the most commonly used parsers (albeit a bit overkill for your use case) is Antlr. Antlr brings out-of-the-box support for Json.
I recommend to take the time to ingest all the involved concepts. Even though it might seem overkill initially, it quickly pays off when you do any kind of extension. Changing a grammar is relatively easy; the generated code is quite complex. Additionally, all parser generator verify your grammars to show logic errors.
Of course, if you are limiting yourself to just parsing CSV or JSON (and not both at the same time), you should rather take the parser of an existing library. For example, jackson has ObjectMapper.readTree to get the parse tree. You could also use ObjectMapper.readValue(<fragment>, Object.class) to simply get the canonical java classes.
Try this :
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
String j = // json string;
JsonFactory jsonFactory = new JsonFactory();
ObjectMapper jsonMapper = new ObjectMapper(jsonFactory);
JsonNode jsonRootNode = jsonMapper.readTree(j);
Iterator<Map.Entry<String,JsonNode>> jsonIterator = jsonRootNode.fields();
while (jsonIterator.hasNext()) {
Map.Entry<String,JsonNode> jsonField = jsonIterator.next();
String k = jsonField.getKey();
String v = jsonField.getValue().toString();
...
}
I want to retrieve information through a library that attacks Codewars API whit my user on JSON format.The request method(GET, gives you back a JSON with information about your user) https://www.codewars.com/api/v1/users/(here you put your user name) .First of all I added my name at the end of this URI to check that it works right and returns a JSON. After that I used a converter (JSON to Java) to get back the Java code corresponding of my JSON. http://www.jsonschema2pojo.org/
My JSON:
{
username: "heremyusername",
name: null,
honor: 2,
clan: "pepito",
leaderboardPosition: 500793,
skills: null,
ranks: {
overall: {
rank: -8,
name: "8 kyu",
color: "white",
score: 2
},
languages: {
java: {
rank: -8,
name: "8 kyu",
color: "white",
score: 2
}
}
},
codeChallenges: {
totalAuthored: 0,
totalCompleted: 1
}
}
My method:
public User getUser() {
RestTemplate restTemplate = new RestTemplate();
User user = restTemplate.getForEntity("https://www.codewars.com/api/v1/users/(here my user name)", User.class).getBody();
return ;
}
Problem is that JSON to Java has given me back 6 differents Java class. One of them:
package entities;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"overall",
"languages"
})
public class Ranks {
#JsonProperty("overall")
private Overall overall;
#JsonProperty("languages")
private Languages languages;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String,
Object>();
#JsonProperty("overall")
public Overall getOverall() {
return overall;
}
#JsonProperty("overall")
public void setOverall(Overall overall) {
this.overall = overall;
}
#JsonProperty("languages")
public Languages getLanguages() {
return languages;
}
#JsonProperty("languages")
public void setLanguages(Languages languages) {
this.languages = languages;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
It give me back another 5 Java class(Java,Overall,Mail,Languages,CodeChallenge) with same format(getters,setters...) So method public User getUser() its wrong, I donĀ“t know what it returns,if I have to make it void or what class to use because I want to retrieve all information not only a portion.
User is a JavaBean whose fields are Integer id and String username.
I have made a class named Entity, and have the following code:
Entity zombie1 = new Entity();
I get input 'zombie' from a scanner, and then concatenate a number, based on level on the end of that, leaving 'zombie1' as the string... I want to be able to use that string and call
zombie1.shoot("shotgun");
but I can't seem to find a solution. I'd just do a if statement but I want to be able to create as many zombies as I want and not have to put in more if statements every single time.
I've read articles using reflection and forString but that doesn't seem to be what i'm looking for.
Any help would be nice.
Possible solutions are to use a Map<String, Entity> to be able to store and retrieve entities based on specific Strings. If you have a limited number of sub-types of Entity such as Zombies, Vampires, Victims, etc, you could have a Map<String, List<Entity>>, allowing you to map a String to a specific type of entity and then get that type by number.
e.g.,
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Foo002 {
private static final String ZOMBIE = "zombie";
public static void main(String[] args) {
Map<String, List<Entity>> entityMap = new HashMap<String, List<Entity>>();
entityMap.put(ZOMBIE, new ArrayList<Entity>());
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "John"));
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "Fred"));
entityMap.get(ZOMBIE).add(new Entity(ZOMBIE, "Bill"));
for (Entity entity : entityMap.get(ZOMBIE)) {
System.out.println(entity);
}
}
}
class Entity {
private String type;
private String name;
public Entity(String type, String name) {
this.type = type;
this.name = name;
}
public String getType() {
return type;
}
public String getName() {
return name;
}
#Override
public String toString() {
return type + ": " + name;
}
}
This is not your best bet. Your best bet is to have a Map;
// PLEASE LOOK INTO WHICH MAP WOULD BE BEST FOR YOUR CASE OVERALL
// HASHMAP IS JUST AN EXAMPLE.
Map<String, Entity> zombieHoard = new HashMap<String, Entity>;
String getZombieID( int id )
{
return String.format( "zombie%s", id );
}
String createZombie() {
String zid = getZombieID( Map.size() );
Map.put( zid, new Entity() );
return zid;
}
void sendForthTheHoard() {
createZombie();
createZombie();
String currentZombie = createZombie();
zombieHoard.get( currentZombie ).shoot( "blow-dryer" );
zombieHoard.get( getZombieID( 1 ) ).eatBrains();
}
Put your zombies in an ArrayList. Example:
ArrayList<Entity> zombies = new ArrayList<Entity>();
Entity zombie1 = new Entity();
zombies.add(zombie1);
Entity zombie2 = new Entity();
zombies.add(zombie2);
etc...
Then when it is time to call a certain zombie to the following:
zombies.get(1).shoot("shotgun");
If you are talking about dynamically invoking a method on an object, you can use Reflection to get the method object and invoke it (Note: I may have inadvertantly mixed up some C# syntax in this Java):
Entity zombie1 = new Entity();
Method shootMethod = Entity.class.getMethod("shoot", new Class[] { string.class });
shootMethod.invoke(zombie1, new Object[] { "shotgun" });