A service I don't control is sending me JSON that looks like this when there is one item:
{ items: {
item: { ... }
}
... like this when there is no item:
{ items: null }
... and like this when there are two or more items:
{ items: {
item: [
{ ... },
{ ... }
]
}
}
I believe this is because the service was designed to produce XML like this:
<items>
<item>....</item>
<item>....</item>
</items>
... and has thrown the results at an XML->JSON convertor without paying much attention to the result.
Using Jackson, I can handle the "sometimes null, sometimes object, sometimes array" issue in Jackson by configuring my ObjectMapper with DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY.
I can handle the two-layer structure by defining an Items class with a field List<Item> item.
However, the Items class is only there as a sop to the weird JSON format. Other than by writing a whole custom deserializer, can I persuade Jackson to deserialize to an object as if the structure was:
{ items: [ { ... }, { ... } ] }
?
It looks like you have a root object which contains Items property. In this case you can use #JsonUnwrapped annotation. See below class:
class Root {
#JsonUnwrapped
private Items items = new Items();
public Items getItems() {
return items;
}
public void setItems(Items items) {
this.items = items;
}
}
Simple usage:
ObjectMapper mapper = new ObjectMapper();
Items items = new Items();
items.setItem(Arrays.asList(new Item(), new Item()));
Root wrapper = new Root();
wrapper.setItems(items);
String json = mapper.writeValueAsString(wrapper);
System.out.println(json);
Above program prints:
{"item":[{"name":"X"},{"name":"X"}]}
Usually the easiest way to handle mapping is to reflect structure of data format in your POJO structure. So in this case, you would add one functionally unnecessary, but mapping-wise necessary, property:
public class POJO {
public ItemHolder items;
// might want convenience method for Java access, but not with getter name
public List<Item> items() {
return (items == null) ? null : items.item;
}
}
public class ItemHolder {
public List<Item> item;
}
and you do still also need to enable DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY to handle mapping from single Item into List<Item>.
But perhaps the other answer of using #JsonUnwrapped also helps; it is another option.
Related
Let's say I want to get a data from Visual Studio TFS and the response (as json) is in this kind of format:
{
"Microsoft.VSTS.Scheduling.StoryPoints": 3.0,
// ......
}
There's dot in the property name. Reading from other questions I found out that I can read that json in typescript by using an interface like this
export interface IStory { // I don't think this kind of interface do me any help
"Microsoft.VSTS.Scheduling.StoryPoints": number
}
And then I can use the property with this syntax:
var story = GetStoryFromTFS();
console.log(story["Microsoft.VSTS.Scheduling.StoryPoints"]);
But I'd prefer not to call the property like this, since the intellisense won't able to help me finding which property I want to use (because I call the property using a string).
In C# there is a JsonProperty attribute which enable me to create a model like this:
public class Story
{
[JsonProperty(PropertyName = "Microsoft.VSTS.Scheduling.StoryPoints")]
public double StoryPoints { get; set; }
}
And then I can use the property this way:
var story = GetStoryFromTFS();
Console.WriteLine(story.StoryPoints);
This way the intellisense will able to help me finding which property I want to use.
Is there something like JsonProperty attribute in typescript? Or is there any other, better way, to achieve this in typescript?
You have many options. Just keep in mind that all of these options require you to pass the original data to the class that will access it.
Map the values.
class StoryMap {
constructor(data: IStory) {
this.StoryPoints = data["Microsoft.VSTS.Scheduling.StoryPoints"];
}
StoryPoints: number;
}
Wrap the data.
class StoryWrap {
constructor(private data: IStory) {}
get StoryPoints(): number { return this.data["Microsoft.VSTS.Scheduling.StoryPoints"] };
}
Build a decorator to map the data.
function JsonProperty(name: string) {
return function DoJsonProperty(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.get = function () {
return this.data[name];
}
descriptor.set = function (value) {
this.data[name] = value;
}
}
}
class StoryDecorator
{
constructor(private data: IStory) {}
#JsonProperty("Microsoft.VSTS.Scheduling.StoryPoints")
get StoryPoints(): number { return 0 };
}
I'm using spring-boot 1.3.3 and trying to figure out how to have root names on JSON serialization. For example, I would like...
{ stores: [
{
id: 1,
name: "Store1"
},
{
id: 2,
name: "Store2"
}]
}
but instead I am getting
[
{
id: 1,
name: "Store1"
},
{
id: 2,
name: "Store2"
}
]
I've been looking at #JsonRootName and customizing the Jackson2ObjectMapperBuilder config but to no avail. In grails, this is pretty simple with Json Views and I was trying also to see how that translated directly to spring-boot but still can't seem to figure it out.
I realize this is similar to this question but I feel like in the context of Spring (boot) it might be applied differently and would like to know how.
SpringBoot use Jackson as json tool, so this will work well:
#JsonTypeName("stores")
#JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
public class stores {
...
}
Solution 1: Jackson JsonRootName
I've been looking at #JsonRootName and
customizing the Jackson2ObjectMapperBuilder config but to no avail
What are your errors with #JsonRootName and the Jackson2ObjectMapperBuilder?
This works in my Spring Boot (1.3.3) implementation:
Jackson configuration Bean
#Configuration
public class JacksonConfig {
#Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToEnable(SerializationFeature.WRAP_ROOT_VALUE);
// enables wrapping for root elements
return builder;
}
}
Reference: Spring Documentation - Customizing the Jackson ObjectMapper
Add #JsonRootElement to your response entity
#JsonRootName(value = "lot")
public class LotDTO { ... }
Json Result for HTTP-GET /lot/1
{
"lot": {
"id": 1,
"attributes": "...",
}
}
At least this works for a response with one object.
I haven't figured out how to customize the root name in a collection. #Perceptions' answer might help How to rename root key in JSON serialization with Jackson
EDIT
Solution 2: Without Jackson configuration
Since I couldn't figure out how to customize the json root name in a collection with Kackson
I adapted the answer from #Vaibhav (see 2):
Custom Java Annotation
#Retention(value = RetentionPolicy.RUNTIME)
public #interface CustomJsonRootName {
String singular(); // element root name for a single object
String plural(); // element root name for collections
}
Add annotation to DTO
#CustomJsonRootName(plural = "articles", singular = "article")
public class ArticleDTO { // Attributes, Getter and Setter }
Return a Map as result in Spring Controller
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Map<String, List<ArticleDTO>>> findAll() {
List<ArticleDTO> articles = articleService.findAll();
if (articles.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
Map result = new HashMap();
result.put(ArticleDTO.class.getAnnotation(CustomJsonRootName.class).plural(), articles);
return new ResponseEntity<>(result, HttpStatus.OK);
}
I found a really easy way to do that. Instead of returning an
ArrayList<Object>
of your objects to the frontend, just return a
HashMap<String, ArrayList<Object>>
and initilize the list of your objects and the according HashMap in the following way:
List<Object> objectList = new ArrayList<>();
... fill your objectList with your objects and then:
HashMap<String, ArrayList<Object>> returnMap = new HashMap<>();
returnMap.put("lot", objectList);
return returnMap;
The head of your controller method looks someting like this:
#ResponseBody
#RequestMapping(value = "/link", method = RequestMethod.POST)
public HashMap<String, List<Object>> processQuestions(#RequestBody List<incomingObject> controllerMethod) {
This will result in the desired JSON without further definitions through annotations or whatever.
I think the easiest way would be to return Map <String, Object>.
Just put the key as 'stores' and object as stores object.
ex-
Map<String, Object> response = new LinkedHashMap<String, Object>();
response.put("stores", stores)
Following this example.
GET response is:
{
"singer":"Metallica",
"title":"Enter Sandman"
}
If more objects were included output should be like this:
[{
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]
I want to get the 'classname' written too. Something like this:
{"Track":[ {
"singer":"Metallica",
"title":"Enter Sandman"
}, {
"singer":"Elvis",
"title":"Rock"
}]}
Any simple ways to achieve this?
Looking forward to get data directly into Datatables from a JAX-RS Resteasy (Jackson) Server. Also trying to avoid DTO.
class TrackList
{
private List<Track> Track = new ArrayList<Track>();
// setter, getter
}
GET method
public TrackList getTrackInJSON() {
EDIT
GET method
public String getTrackInJSON() {
// ... create list of objects
return convertToString(objects);
}
utility method
static <T> String convertToString(List<T> list) throws IOException
{
final String json = new ObjectMapper().writeValueAsString(list);
return new StringBuilder()
.append("{\"")
.append(list.get(0).getClass().getSimpleName())
.append("\":")
.append(json)
.append("}")
.toString();
}
I have JSON string that has nested objects with dynamic names that vary each time. For an instance:
{
"Objects": {
"dynamicName1": {
"name": "test"
},
"dynamicName2": {
"name": "test"
}
}
}
I was wondering how can you deserialize this string in APEX using wrapper classes?
I tried this:
public class masterobj
{ public childobj Objects;
}
public class childobj
{ public el dynamicName1;
public el dynamicName2;
}
public class el
{ public string name;
}
String s = '{"Objects":{"dynamicName1":{"name":"test"},"dynamicName2":{"name":"test"}}}';
masterobj mo = (masterobj)JSON.deserialize(s, masterobj.class);
which works well when you have declared the dynamic variable names in the class for each nested object.
The problem and the question is how can I make this work using a dynamic variable in the wrapper class. Because the object names will vary and also the number of the objects, I can't hard-code the names as they are different each time.
Any ideas?
You won't be able to deserialize a structure like that with the data binding features of the json parser, you'll need to use the streaming json parser to read it.
Use a Map:
public class masterobj
{
Map<String, el> Objects;
}
My Problem is: I don't want to return the whole model object structure and datas in a json response. renderJSON() returns everything from the model in the response.
So I thought the best way would be to use *.json templates. Ok and now I have a List and I don't now how the syntax in the *.json-template must be.
Part of Controller:
List<User> users = User.find("byActive", true).fetch();
if (users != null) {
render(users);
}
"User/showAll.json" (template):
//something like foreach( User currentUser in users )
{
"username": "${currentUser.name}",
"userdescr": "${currentUser.description}"
}
For a single user it's no problem, I got it, but how does it look like for a List of users inside the json template?
There is another solution to your problem without using .json template.
"renderJSON()" has a variation takes JsonSerializer as parameter, so you can define your own serializer which implements JsonSerializer, and decide what part of model object to be sent in the response. Then you could invoke renderJSON() to return JSON object in the controller.
Example:
public class UserSerializer implements JsonSerializer<User> {
public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
Gson gson = new GsonBuilder()
.setExclusionStrategies(new LocalExclusionStrategy()).create();
return gson.toJsonTree(src);
}
public static class LocalExclusionStrategy implements ExclusionStrategy {
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
public boolean shouldSkipField(FieldAttributes f) {
// add exlusion rules here:
// exclude all fields whose name is not "name" or "description"
return !f.getName().toLowerCase().equals("name")
&& !f.getName().toLowerCase().equals("description");
}
}
}
In your controller:
List<User> users = User.find("byActive", true).fetch();
renderJSON(users, new UserSerializer());
Play framework utilizes Google's GSON library for json serialization/deserialization
You can find more info of GSON here
[
#{list users}
{
"username": "${_.name}",
"userdescr": "${_.description}"
} #{if !_isLast},#{/if}
#{/list}
]
Check http://www.playframework.org/documentation/1.2.4/tags#list for more information