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.
Related
I'm hoping someone can help me. I've been stuck on this for a while now. I am reading a JSON from an API but I have not been successful in converting it and sending it back to my typeahead/autocomplete class. I have attempted several different ways of doing this including JSON Serializable until it wouldn't work and I figured out it won't do nested JSON. The JSON I am reading is not like any of the examples that I have found. I have watched multiple tutorials and read all over stackoverflow. The key value pair I need to read has a key as normal but the value is a list of strings. All of the examples I have found have an object with a key:value pair in the list[]. Can someone please tell me how to read and decode this the easiest way?
Here is an example of the exact JSON:
callback(
{
"status": {
"code": 0
},
"total": 6,
"dictionary_terms": {
"compound": [
"aspirin",
"Aspirine",
"Aspirin sodium",
"Aspirin anhydride",
"Aspirin methyl ester",
"Aspirin calcium"
]
}
}
)
Once you have the object that json.decode(callback) gives, you have a Map<String, dynamic>. So to access compounds in there:
_dynamicMap = json.decode(callback);
List<String> dictionary_terms_compound = _dynamicMap['dictionary_terms']['compound'];
Depending on where you are in null safety, you probably need to either check to make sure each key isn't null. ie, this would fail if dictionary_terms or compound don't exist...so you would need to check for it before you can get the value from it.
So assuming they exist, you might need to put:
List<String> dictionary_terms_compound = _dynamicMap['dictionary_terms']!['compound']!;
The dart definition of your dictionary_terms object is a
Map<String, Map<String,List<String>>>
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.
Apologies for the confusing title, could not come up with a better one.
My API returns a response like this :
{
"type":"a",
"extras":{
"a1":"valueA1",
"a2":"valueA2"
}
}
If the type is "b", then response is like :
{
"extras":{
"b1":"valueB1"
},
"type":"b" //note that "type" may come after "extras"
}
For a third type "c", response can be :
{
"type":"c",
"extras":{
"b1":"valueB1",
"a1":"valueA1",
"a2":"valueA2"
}
}
Following are my requirements :
There are over 20 such "types" which the API can return.
Each type has an extra node with attributes which may or may not be shared with other typeExtras. Typically 1-10 attributes in each extra node.
Order of "type" and "extras" is not fixed. But both are guaranteed to be present.
The naive solution I am trying is to have one "extras" POJO with all possible attribute values :
Extras.java
String a1;
String a2;
String b1;
My clients have checks like this during consumption:
if(type.isA()) {
a1 = extras.a1;
a2 = extras.a2;
//there is no way to prevent developers from calling extras.b1
}
My code is bloated with over 50 attributes and littered with null checks for 20 types. Is there a better way ?
Bonus question : I strongly feel that the API is poorly designed. Are there any second thoughts ?
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).
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.