I'm writing a library to deserialize a subset of JSON into predefined Python types.
I want to deserialize arbitrary JSON into an object that quacks like serde-json's Value. However, I don't want it to deserialize into String's, Number's and Bool's - instead when the deserializer hits one of these I would prefer it simply keeps a reference to the respective byte string so I can efficiently (i.e. without the additional type conversion) parse the byte strings into the correct arbitrary Python types. Something like this:
use serde::Deserialize;
use serde_json::value::RawValue;
use serde_json::Map;
#[derive(Deserialize)]
pub enum MyValue<'a> {
Null,
Bytes(&'a RawValue),
Array(Vec<MyValue<'a>>),
Object(Map<String, MyValue<'a>>),
}
This will require writing a lot of traits so that it behaves like Value, and I'm not even sure if it won't just ignore deserializing the structural parts and put everything into a RawValue.
What is the cleanest way to do this?
I want to wrap data to JSON before sending.
func request(content: [String]) {
try? JSONEncoder().encode(content)
This worked well when sending arrays of string. However, I would also like to send other structures that can be represented in JSON.
For example arrays containing arrays, or dictionaries.
My idea was to make it
func request(content: Any) {
try? JSONEncoder().encode(content)
Which gives Protocol 'Any' as a type cannot conform to 'Encodable'
How can I make the parameter as generic as possible so what can be formed as JSON gets formed as JSON and whatever fails, fails? I do understand I can't JSONify everything but I would like to be able to do it with things that I know of can be represented in JSON form. I found things like this https://stackoverflow.com/a/64471720/2161301 but apparently you can't do that on "normal" functions
You can use AnyCodable in your project.
Then you will be allowed to use [String: AnyEncodable] / [AnyEncodable] etc. in the places where you are not able to use Any while trying to use JSONEncoder api.
I'm trying to retrieve data from an API call and pass them to another service. The data i received are in a specific JSON structure and i would like to map it to a struct, but without the multiple levels of data. I tried the dot notation to access deeper value, but it doesn't work.
Basically, i'm trying to get a struct with an array of "issues" (key, self, description), but without having the "fields.description" structure.
The JSON:
{
"ticket": {
"number": "2",
"issues": [
{
"key": "TA-2",
"self": "http://localhost:8080/rest/api/2/issue/10100",
"fields": {
"description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|personal_project|Enter the name of your openshift project|\n|memory|8GB|Enter the desired amount of memory|"
}
},
{
"key": "TA-1",
"self": "http://localhost:8080/rest/api/2/issue/10000",
"fields": {
"description": "This template is used to create openshift project.\n|Type|Value|Help|\n|project_name|my_openshift_project|Enter the name of your openshift project|\n|memory|4GB|Enter the desired amount of memory|"
}
}
]
}
}
The Struct:
type Todo struct {
Number string `json:"number"`
Issue []struct {
Key string `json:"key"`
Self string `json:"self"`
Description string `json:"field.description"` //doesn't work. don't know what to put ...
} `json:"issues"`
}
The expected / desired struct:
{
"number": "2",
"issues": [{
"key": "TA-2",
"self": "http://localhost:8080/rest/api/2/issue/10100",
"description": "This template ..."
}, {
"key": "TA-1",
"self": "http://localhost:8080/rest/api/2/issue/10000",
"description": "This template ..."
}]
}
Is it possible? if yes, how to do it? Using nested struct won't change the initial JSON structure.
Thanks
You have at least three options:
Create a separate set of struct types to represent the data in the format provided by the JSON. This might include a TodoList struct with a ticket field, referring to a separate Todo struct. This struct might have a number field and an issues field. Each Issue struct might have a fields field, which in turn has a description field. Basically, you recreate the entire domain as represented in the JSON, then you can simply call json.Unmarshal() to unmarshal the JSON into the TodoList. Once you have a TodoList, simply convert it to the representation that you desire by hand.
Write your own custom UnmarshalJSON() function for the Todo and Issue structs. Click here for an example from the GoLang docs.
Unmarshal the entire JSON document manually in an external function. This might involve unmarshalling the data to a map[string]json.RawMessage, and then mapping many of those json.RawMessage objects to other maps and objects, and so on.
The first option is better for cases where you simply have an alternate representation of the data which you desire, but all of the data is still in the JSON document, and all of the data in the JSON document is still relevant. This seems to match your predicament, and so this is the option I would recommend.
The second option is better for cases where you would simply like to recognize that the JSON encoding for a structure should provide a consistently different representation from the GoLang object encoding. In other words, when the structure is encoded as a GoLang object, it should take one form, whereas when the structure is encoded in JSON, it should take another form. These two forms must be strictly tied to their encodings (JSON vs GoLang object). If you plan on unmarshaling the JSON to a GoLang object, and then marshaling it back into a different JSON representation from what you had to begin with, this is probably not the best option because json.Unmarshal() and json.Marshal() should be inverses of each other. Otherwise, you have violated the principle of least astonishment. This option can also be used for delaying or preventing unmarshaling of nested properties by way of json.RawMessage objects. Thus, if you have some dynamically typed JSON field whose type is determined upon examination of other fields, or if you have large amounts of irrelevant data in the JSON document, you can delay or prevent the unmarshalling of such data until desired.
In practice, the third option is very similar to the second. However, by providing an external unmarshaling mechanism rather than implementing UnmarshalJSON(), you are less likely to break the principle of least astonishment. That is, the perceived contract between json.Unmarshal() and json.Marshal() is necessarily preserved. Thus, you can marshal and unmarshal data however you would like, and you can maintain as many different representations per encoding as you would like. That being said, the first option provides this very same benefit, and it is much easier. In reality, the third option should only ever be chosen if the first and second options will not suffice, such as when you may need to delay or prevent unmarshaling of nested properties (like in the second option) while simultaneously providing multiple different JSON representations and preserving the contract between json.Marshal() and json.Unmarshal() (like in the first option).
You cannot add a dot notation in json struct tags. If you expect multiple fields under fields and if you are sure that each field value is a string, then you can unmarshal your JSON into
type Todo struct {
Number string `json:"number"`
Issue []struct {
Key string `json:"key"`
Self string `json:"self"`
Fields map[string]string `json:"fields"`
} `json:"issues"`
}
If its only description everytime, then you can add
Fields struct {
Description string `json:"description"`
} `json:"fields"`
I have a hard time deserializing (using Rust's serde and serde_json v1.0) the following JSON I receive:
{
["string content"]
}
The object's array is not identified by a key, so the following doesn't work:
#[derive(Deserialize)]
struct Data {
key: Vec<String>
}
I've also tried using #[serde(flatten)] on the key field but I get an error:
can only flatten structs and maps (got a sequence)
The data I receive doesn't look like valid JSON. Is it still possible using serde_json?
The input you show is not valid JSON. You will not be able to use serde_json to deserialize that input because serde_json only accepts JSON.
If you find out what format the data is intended to be in, consider using (or writing) a Rust library dedicated to that specific format.
I want to deserialise an object that includes an array of a some interface Entity:
type Result struct {
Foo int;
Bar []Entity;
};
Entity is an interface that is implemented by a number of struct types. JSON data identifies the struct type with a "type" field in each entity. E.g.
{"type":"t1","field1":1}
{"type":"t2","field2":2,"field3":3}
How would I go about deserialising the Result type in such a way that it correctly populates the array. From what I can see, I have to:
Implement UnmarshalJSON on Result.
Parse Bar as a []*json.RawMessage.
Parse each raw message as map[string]interface{}.
Check "type" field in the raw message.
Create a struct of appropriate type.
Parse the raw message again, this time into the just created struct.
This all sounds very tedious and boring. Is there a better way to do this? Or am I doing it backwards, and there is a more canonical method to handle an array of heterogeneous objects?
I think your process is probably a bit more complicated than it has to be, see http://play.golang.org/p/0gahcMpuQc. A single map[string]interface{} will handle a lot of that for you.
Alternatively, you could make a type like
struct EntityUnion {
Type string
// Fields from t1
// Fields from t2
// ...
}
Unmarshal into that; it will set the Type string and fill in all the fields it can get from the JSON data. Then you just need a small function to copy the fields to the specific type.