Can you flatten a nested JSON structure into a single autobean? - json

Assuming the following JSON structure:
{
\"is_something\": false,
\"name\": \"Some Name\",
\"subtype\": {
\"total\": 0.0
}
}
Instead of creating two autobean interfaces (one for the whole structure and one for the subtype), I would like to have one which contains all the properties.
public interface ExampleAutoBean {
#PropertyName("is_something")
boolean isSomething();
String getName();
#PropertyName("subtype.total")
double getTotal();
}
So, the getTotal() method is expected to contain the total property of the nested subtype in the JSON structure. I can't find any documentation in the source code or online which states whether or not this is possible.
Thanks in advance!

Nope: AutoBeans are designed to be a mapping from the JSON structure to Java interfaces, plus or minus collections like List, Set, and Map and String encodings of a long or a Date. Additionally, it is legal to have json like the following:
{
"some.property.with.dots" : "abcd",
"name" : "wxyz"
}
If the . character could only be used for traversing into sub-objects, there would be no way to have a getter for the first property.

Related

Dynamic tag in JSON

I am generating one JSON file and I have a variable of the below type in TypeScript:
export interface Rules{
name: string,
condition: string,
}
I want to parse the 'Rule' type in JSON in the below format:
"dataRetentionRules": {
"RULE_1": {
"name": "test123",
"condition": "RetentionStartDate"
},
"RULE_2": {
"name": "test456",
"condition": "RetentionEndDate"
}
}
How can I make this rule tag dynamic? I mean how do I parse this "RULE_1", "RULE_2" tag? Since tags are supposed to be constant I believe. How can I achieve the above format?
You can make use of the typescript Record built-in type. Behind the scenes you are using the principles of index types and mapped types.
But all that you really need to know it this: Record<string, Rules> defines an object whose keys are strings and whose values are instances of Rules.
Your JSON object can be described by
interface HasRetentionRules {
dataRetentionRules: Record<string, Rules>
}
Note that this is asserting that any string key corresponds to a Rules object and it does not check for invalid keys, so you need to check for invalid keys yourself. Alternatively, you can use Partial<Record<string, Rules>> which states that any string key has a value which is of type Rules or undefined.
Typescript Playground Link

TJSONUnMarshal: how to track what is actually unmarshalled

Is there another way to track what is unmarshalled than write own reverter for each field?
I'm updating my local data based on json message and my problem is (simplified):
I'm expecting json like
{ "items": [ { "id":1, "name":"foobar", "price":"12.34" } ] }
which is then unmarshaled to class TItems by
UnMarshaller.TryCreateObject( TItems, TJsonObject( OneJsonElement ), TargetItem )
My problem is that I can't make difference between
{ "items": [ { "id":1, "name":"", "price":"12.34" } ] }
and
{ "items": [ { "id":1, "price":"12.34" } ] }
In both cases name is blank and i'd like to update only those fields that are passed on json message. Of course I could create a reverted for each field, but there are plenty of fields and messages so it's quite huge.
I tried to look REST.Jsonreflect.pas source, but couldn't make sense.
I'm using delphi 10.
In Rest.Json unit there is a TJson class defined that offers several convenience methods like converting objects to JSON and vice versa. Specifically, it has a class function JsonToObject where you can specify options like for example ignore empty strings or ignore empty arrays. I think the TJson class can serve you. For unmarshalling complex business objects you have to write custom converters though.
Actually, my problem was finally simple to solve.
Instead of using TJSONUnMarshal.tryCreateObject I use now TJSONUnMarshal.CreateObject. First one has object parameters declared with out modifier, but CreateObject has Object parameter var modifier, so I was able to
create object, initalize it from database and pass it to CreateObject which only modifies fields in json message.

Is this json badly constructed?

The following json text is the result of a call to eBay's search API.
Q: Is this json badly constructed? (It's technically, correct - so that's not what I'm referring to)
By that I mean, notice how -every value- is inside an array? instead of just being a "key" : "value" ?
eg.
"ack": [
"Success"
],
or
"version": [
"1.12.0"
],
etc..
Now, before you answer "well, maybe each key has multiple result values" .. I'm pretty sure most of them can't.
(this json schema is really making my life a pita. yes, it's easy to make each POCO property a List<string> but it's the point of it all!)
References: Here's the office eBay API documention for this endpoint
After reading the documentation, I understand eBay's approach, but I find it poor in terms of client side deserialisation. For example, the benefit of an array value for the ack property is that the value could also contain a warning, e.g.:
{
"ack": [
"Success",
"Warning"
]
}
However, a list of strings is not ideal for client side processing (e.g. in C#, bool hasWarning = pocoList.Contains("Warning"); doesn't strike me as completely foolproof). I'd rather have a response such as:
{
"ack": {
"result": "Success",
"warning": null
}
}
Then with my deserialised POCO, given that the value of warning is still a string, I could write this:
[DataContract]
public class Ack
{
[DataMember(Name="result")]
public string Result { get; set; }
[DataMember(Name="warning")]
public string Warning { get; set; }
[IgnoreDataMember]
public bool HasWarning
{
get { return !string.IsNullOrEmpty(Warning); }
}
}
This would allow me to replace my previous LINQ query with bool hasWarning = ack.HasWarning;.
There are definitely places where use of arrays is completely unnecessary. The docs describe the version property as "the release version that eBay used to process the request", thus I would return this as a single string. The array would only make sense if it was a versions property (e.g. for identifying all versions of the backend that support a specific request).
I've definitely seen worse JSON responses, but there are places where the APIs should ideally be returning a JSON object or a single value.
I normally use this tool for see if a JSON is good constructed:
http://json.parser.online.fr/
In your case, is not correct. You should store the data by Keys and Values.

Why is GSON not parsing these fields properly? (FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)

I have a JSONArray of JSONObjects that I'm trying to parse with GSON. I'm using FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES. It's parsing correctly for most fields (so the FieldNamingPolicy is set correct), but I'm getting null returned for
{
"image_sq_48x48_url": "url1",
"image_sq_64x64_url": "url2",
"image_sq_96x96_url": "url3"
}
with field names
imageSq48x48Url
imageSq64x64Url
imageSq96x96Url
Maybe a better question would be what is the proper camelCase? I have also tried
imageSq48X48Url
imageSq48X48url
If I map with #SerializedName("image_sq_96x96_url") it parses/populates correctly.
Unfortunately those fieldnames in your JSON don't conform to what Gson looks for using that strategy.
If you create a POJO and serialize it, you can see what the issue is:
class MyPojo
{
String imageSq48x48Url = "hi";
}
The resulting JSON from Gson using that strategy is:
{"image_sq48x48_url":"hi"}
It doesn't consider/look at numeric digits as leading indicators / start of a "word".
If you rename the field to:
String imageSq_48x48Url;
It would work with your JSON example and that strategy.
Basically, you either need to create your own class that implements FieldNamingStrategy that will handle those JSON fieldnames the way you want, or do what you're doing with the #SerializedName annotation.

How can you use GWT AutoBeans to parse a JSON message when some of the return values could be an object or a collection of objects?

Is it possible to parse a JSON message using GWT AutoBeans when one of the objects returned may be a collection but not always?
For example, if I have a JSON message returning an author and his/her associated writings, it's possible that there could be zero or more books being returned.
{ "name" : "William Gibson", "books" : { bookname : "Neuromancer" } }
could be one response, but so could this:
{ "name" : "William Gibson", "books" : [ { bookname: "Neuromancer"}, { bookname : "Pattern Recognition" } ] }
When I attempt to model this with an interface to be used for marshalling with an AutoBean, I get "expecting indexed data" errors if only one book is returned.
Interface for the AutoBean:
public interface Author {
#PropertyName(value="name")
String getAuthorName();
#PropertyName(value="book")
List<String> getBooks();
}
Snippet of error:
java.lang.AssertionError: Expecting indexed data
at com.google.web.bindery.autobean.shared.impl.SplittableList.<init>(SplittableList.java:64)
Is this not possible with AutoBeans?
(Note: using GWT 2.5.0 GA)
If you have a List, AutoBeans expects a JSON array. That array could contain zero, one or more elements, but it has to be an array (or be absent).
I think you can make your getBooks method return a Splittable though. You could then know whether it's an array (isIndexed()) or not. If you need the array to contain objects, you'd then have to iterate on the array (size() and get(int)) and pass each element to AutoBeanCodex.decode() to decode them (or directly pass the splittable if it's not an array).