I have a JSON string like this:
{
"123": {
"hi": {
"name": "John",
"phone": "12345"
}
},
"124": {
"hi": {
"name": "James",
"phone": "12345"
}
},
"125": {
"hi": {
"name": "Leo",
"phone": "12347"
}
}
}
The JSON is ordered externally: 123, 124, 125 I just want to get the value of the last field ("125" : {...}) but I don't know its name, is auto-generated. What's the best way?
I'm trying mapping to an arrayList of JsonNodes, but I don't find the right way.
You can also do it using #JsonAnySetter annotation. Please, see below example. POJO class:
class Node {
private Integer key = Integer.MIN_VALUE;
private JsonNode value;
#JsonAnySetter
public void set(String newKey, JsonNode newValue) {
if (Integer.valueOf(newKey) > key) {
value = newValue;
}
}
public JsonNode getValue() {
return value;
}
#Override
public String toString() {
return String.valueOf(value);
}
}
Example usage:
ObjectMapper mapper = new ObjectMapper();
Node node = mapper.readValue(json), Node.class);
System.out.println(node);
Prints:
{"hi":{"name":"Leo","phone":"12347"}}
I did it putting the JSON string in a JsonNode, data and mapping to a NavigableMap.
NavigableMap<String, JsonNode> updates = mapper.readValue(data.traverse(), new TypeReference<TreeMap<String, JsonNode>>() {});
Entry<String, JsonNode> lastEntry = updates.lastEntry();
String lastUpdate = lastEntry.getValue().toString();
Related
I have a small problem regarding the marshalling via JAXB.
Currently I have a HashMap of Objects
#XmlJavaTypeAdapter(HashMapAdapter.class)
private Map<String, Object> data;
beeing marshalled by the Custom HashMapAdapter
public class HashMapAdapter extends XmlAdapter<HashMapAdapter.AdaptedMap,
Map<String, Object>> {
#XmlRootElement
public static class AdaptedMap {
#XmlVariableNode("key")
List<Data> entries = new ArrayList<>();
}
public static class Data {
#XmlTransient
public String key;
#XmlValue
public Object value;
}
#Override
public Map<String, Object> unmarshal(AdaptedMap v) throws Exception {
throw new NotImplementedException();
}
#Override
public AdaptedMap marshal(Map<String, Object> map) throws Exception {
AdaptedMap adaptedMap = new AdaptedMap();
for (Map.Entry<String, Object> entry : map.entrySet()) {
Data data = new Data();
data.key = entry.getKey();
data.value = entry.getValue();
adaptedMap.entries.add(data);
}
return adaptedMap;
}
}
The Marshalling is based on the following Post: http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-using-maps-key-as.html
The HashMap is filled with either Boolean, Long or String Values.
So regarding the Blog the expected JSON Output should be:
"data": {
"booleanValue": true,
"stringValue": "test",
"longValue": 1234
}
But the real outcome is:
"data": {
"longValue": {
"type": "long",
"value": 1234
},
"stringValue": {
"type": "string",
"value": "test"
},
"booleanValue": {
"type": "boolean",
"value": true
}
}
Im running on Payara Micro 174 and therefore on MOXy as JAXB provider.
Is it possible to get rid of the "type"/"value" nesting?
Best Regards
Simon
I do not have an MOxY implementation handy, could you try this and tell me if it works ?
public static class Data {
#XmlTransient
public String key;
#XmlElements({
#XmlElement(type=Long.class),
#XmlElement(type=String.class),
#XmlElement(type=Boolean.class)
})
#XmlPath(".")
public Object value;
}
EDIT :
The Output you get when using this Approach is:
"data": {
"stringValue": {
"value": test
},
"booleanValue": {
"value": true
},
"longValue": {
"value": 1234
}
}
Sadly this differs a little from the expected.
"statValues": {
"c__TL_gattooi": {
"value": 90.0
},
"c_cwc_gattooi": {
"value": 3462.0
},
"c_gaw__oxcgattooi": {
"value": 11.0
},
"c_odesb__ox_gattooi": {
"value": 6.0
},
"c_odesb_cwdc_gattooi": {
"value": 205472.0
},
"c_ach38_sax_gattooi": {
"value": 1.0
},
}
Want to convert this JSON to a POJO to be mapped with jackson
To deserialize given json from string jsonSource use something like
ObjectMapper mapper = new ObjectMapper();
Root root = mapper.readValue(jsonSource, Root.class);
I generated POJOs for you
import java.util.HashMap;
class Root {
private HashMap<String, Value> statValues = new HashMap<>();
public HashMap<String, Value> getStatValues() {
return statValues;
}
public void setStatValues(HashMap<String, Value> statValues) {
this.statValues = statValues;
}
}
class Value {
double value;
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
}
If can give better names it's possible by remapping properties using #JsonProperty
To access value use something like root.get("c__TL_gattooi").getValue(), to get list of keys - root.keys()
Need to deserialize a JSON structure like below using Jackson, this is a response from a REST API and I am using Spring RestTemplate with MappingJackson2HttpMessageConverter to parse it.
Problems:
1. All elements can have one or more child elements of its own type (e.g. root has children : childABC & childAnyRandomName)
2. Child nodes do not have a standard naming convention, names can be any random string.
{
"FooBar": {
"root": {
"name": "test1",
"value": "900",
"childABC": {
"name": "test2",
"value": "700",
"childXYZ": {
"name": "test3",
"value": "600"
}
},
"childAnyRandomName": {
"name": "test4",
"value": "300"
}
}
}
}
As per my understanding, a POJO to store this could be something like this:
import java.util.Map;
public class TestObject {
private String name;
private String value;
private Map<String, TestObject> children;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public Map<String, TestObject> getChildren() {
return children;
}
public void setChildren(Map<String, TestObject> children) {
this.children = children;
}
}
I have no control over the actual JSON that I am parsing so If Jackson does not work, I will have to push all of it in JsonNode and build this object structure with custom logic.
Disclaimer: I am relatively new to Jackson, so please don't mind if this was a classic text book solution that I didn't read, just point me to the documentation location.
In your bean, have the attributes you know are there:
private String blah;
#JsonProperty("blah")
#JsonSerialize(include = Inclusion.NON_NULL)
public String getBlah() {
return blah;
}
#JsonProperty("blah")
public void setBlah(String blah) {
this.blah = blah;
}
Then, add the following, which is kinda like a catch-all for any properties which you haven't explicitly mapped that appear on the JSON:
private final Map<String, JsonNode> properties = new HashMap<String, JsonNode>();
#JsonAnyGetter
public Map<String, JsonNode> getProperties() {
return properties;
}
#JsonAnySetter
public void setProperty(String key, JsonNode value) {
properties.put(key, value);
}
Alternatively, just map known complex objects to Map fields (IIRC, complex objects under that will also end up in maps).
I have a server that produces the following JSON with Jackson.
{
"$id" : 1,
"employees" : [
{
"$id" : 2,
"name" : "John Rambo",
},
2 // Jackson: reference by ID only
]
}
The list of employees contains the same employee twice. Jackson correctly references the object by it's ID the second time.
I want to deserialize this in a client that uses JSON.net, but this won't work because JSON.net expects me to have the reference wrapped in a json object with a $ref property like this:
{
"$id": "1",
"employees" : [
{
"$id": "2",
"name": "John Rambo"
},
{
"$ref": "2" // JSON.net: reference wrapped in JSON object
}
]
}
Is there a way to make JSON.net correctly consume the Jackson syntax either by configuration or by implementing a custom deserializer?
Here's a custom converter that should work:
public class EmployeeConverter : JsonConverter
{
public override void WriteJson(
JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
List<Employee> employees = null;
if (reader.TokenType == JsonToken.StartArray)
{
JArray arr = serializer.Deserialize<JArray>(reader);
employees = new List<Employee>(arr.Count);
var employeeMap = new Dictionary<int, Employee>();
foreach (var item in arr)
{
if (item.Type == JTokenType.Object)
{
var employee = item.ToObject<Employee>();
employees.Add(employee);
int id = item["$id"].ToObject<int>();
employeeMap.Add(id, employee);
}
else if (item.Type == JTokenType.Integer)
{
Employee employee = null;
int id = item.ToObject<int>();
if (employeeMap.TryGetValue(id, out employee))
{
employees.Add(employee);
}
}
}
}
return employees;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanConvert(Type objectType)
{
return false;
}
}
... and here's how you'd use it:
public class Company
{
public Company()
{
this.Employees = new List<Employee>();
}
[JsonConverter(typeof(EmployeeConverter))]
public List<Employee> Employees { get; set; }
}
Example: https://dotnetfiddle.net/XooyQC
Basically use a custom converter to deserialize the entire array. First, deserialize the array to a JArray, then inspect each element of the JArray to see if it's a reference or a new object.
I have a JsonNode which contains the following JSON. Inside that JsonNode object is an array. In that array there are three fields, one of which, slaid, is a list. The other two are strings. Here is the JSON.
{
"SLA": [
{
"slaid": [
"53637cc144ae8b607e089701"
],
"ragindicator": "Red",
"name": "r1"
},
{
"slaid": [
"53637d1844ae8b607e089704"
],
"ragindicator": "Amber",
"name": "a1"
},
{
"slaid": [
"53637eac44ae8b607e089706"
],
"ragindicator": "Green",
"name": "g1"
}
]
}
I want to parse this value. How can I parse it , where slaid's type is List<String>? I have tried some ways but I am still unable to find the solution.
The easiest way I can see is creating POJO classes which fit to your JSON:
class Slaids {
#JsonProperty("SLA")
private List<Slaid> slaids;
public List<Slaid> getSlaids() {
return slaids;
}
public void setSlaids(List<Slaid> slaids) {
this.slaids = slaids;
}
#Override
public String toString() {
return slaids.toString();
}
}
class Slaid {
private List<String> slaid;
private String ragindicator;
private String name;
public List<String> getSlaid() {
return slaid;
}
public void setSlaid(List<String> slaid) {
this.slaid = slaid;
}
public String getRagindicator() {
return ragindicator;
}
public void setRagindicator(String ragindicator) {
this.ragindicator = ragindicator;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "Slaid [slaid=" + slaid + ", ragindicator=" + ragindicator + ", name=" + name + "]";
}
}
Simple usage:
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(json, Slaids.class));
Above program prints:
[Slaid [slaid=[53637cc144ae8b607e089701], ragindicator=Red, name=r1], Slaid [slaid=[53637d1844ae8b607e089704], ragindicator=Amber, name=a1], Slaid [slaid=[53637eac44ae8b607e089706], ragindicator=Green, name=g1]]
If you want to use JsonNode you can do it in this way:
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(json);
ArrayNode slaidsNode = (ArrayNode) rootNode.get("SLA");
Iterator<JsonNode> slaidsIterator = slaidsNode.elements();
while (slaidsIterator.hasNext()) {
JsonNode slaidNode = slaidsIterator.next();
System.out.println(slaidNode.get("slaid"));
System.out.println(slaidNode.get("ragindicator"));
System.out.println(slaidNode.get("name"));
}
Above program prints:
["53637cc144ae8b607e089701"]
"Red"
"r1"
["53637d1844ae8b607e089704"]
"Amber"
"a1"
["53637eac44ae8b607e089706"]
"Green"
"g1"