In context of this tutorial on F# snippets http://www.fssnip.net/1l/title/Convert-an-object-to-json-and-json-to-object
Let's say a Person type
type Person = {
entityName: string;
entityType: string;
}
and the code to call the web service and convert into json.
let internal json<'t> (myObj:'t) =
use ms = new MemoryStream()
(new DataContractJsonSerializer(typeof<'t>)).WriteObject(ms, myObj)
Encoding.Default.GetString(ms.ToArray())
let internal unjson<'t> (jsonString:string) : 't =
use ms = new MemoryStream(ASCIIEncoding.Default.GetBytes(jsonString))
let obj = (new DataContractJsonSerializer(typeof<'t>)).ReadObject(ms)
obj :?> 't
let requestToken (): Token =
let url = "http://example.com"
let request = WebRequest.Create(url) :?> HttpWebRequest
request.Method <- "POST"
request.Accept <- "application/json;charset=UTF-8"
let response = request.GetResponse() :?> HttpWebResponse
use reader = new StreamReader(response.GetResponseStream())
let body = reader.ReadToEnd()
Console.WriteLine body // result OK
let result = unjson<Person> body
JSON
{
"entityName": "john doe",
"entityType": "client"
}
Error
System.Runtime.Serialization.SerializationException: The data contract type 'Person' cannot be deserialized because the required data members 'entityName#, entityType#' were not found.
if someone can add an example on how to call 'json' function passing result object to convert the object back into the JSON string
Is the Person type required to have all fields as per JSON schema or can I choose to leave out non-required fields?
This will serialize your record to JSON using the DataContractSerializer. You need to add some attributes for this to work.
#r "System.Runtime.Serialization"
open System.IO
open System.Runtime.Serialization.Json
open System.Runtime.Serialization
[<DataContract>]
[<CLIMutable>]
type Person = {
[<DataMember(Name="Name") >]
entityName: string
[<DataMember(Name="Type") >]
entityType: string
}
let person = {entityName = "ENTITY"; entityType ="TYPE"}
let toJson<'t> (myObj:'t) =
let fs = new FileStream(#"C:\tmp\test.json",FileMode.Create)
(new DataContractJsonSerializer(typeof<'t>)).WriteObject(fs,myObj)
toJson<Person> person
And this is the output I get in the test.json file:
{"Name":"ENTITY","Type":"TYPE"}
Related
I've managed to extract data from a POST method in hyper using the following:
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server};
use std::convert::Infallible;
use std::net::SocketAddr;
use tokio;
async fn handle(_req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
match (_req.method(), _req.uri().path()) {
(&Method::GET, "/") => Ok(Response::new(Body::from("this is a get"))),
(&Method::POST, "/") => {
let byte_stream = hyper::body::to_bytes(_req).await?;
let _params = form_urlencoded::parse(&byte_stream)
.into_owned()
.collect::<HashMap<String, String>>();
However, the whole JSON body is just one key in the HashMap now. How do I split it up so I have a hashmap with multiple keys and values as opposed to one key that's the entire body?
[dependencies]
futures = "0.1"
hyper = "0.13"
pretty_env_logger = "0.3.1"
url = "2.1.1"
tokio = { version = "0.2", features = ["macros", "tcp"] }
bytes = "0.5"
There is a discrepancy between your description:
However, the whole JSON body
And your code:
let _params = form_urlencoded::parse(&byte_stream)
If your data is JSON then parse it as JSON, using the serde_json crate:
let _params: HashMap<String, String> = serde_json::from_slice(&byte_stream).unwrap();
I've been struggling with this for long, I believe there's something wrong with my record design.
MY json looks like below. I posted this earlier as a part of another question but didn't get full answer especially for this part. Each record within the array is an object with a field name with data of type string.
JSON
[
{
"data": "/publication/a40a5e5c-98b3-45ae-d6a3-924b31d8712a/article/test;version=1521246543034"
},
{
"data": "/publication/a40a5e5c-98b3-45ae-d6a3-924b31d8712a/book/test2;version=1520623346891"
},
{
"data": "/publication/a40a5e5c-98b3-45ae-d6a3-924b31d8712a/catalog/test3;version=1520623346833"
}
]
List.fs
open System.Runtime.Serialization
[<DataContract>]
type List= {
[<field: DataMemberAttribute(Name="data") >]
Data: string
}
Parsing JSON
let response = request.GetResponse() :?> HttpWebResponse
use reader = new StreamReader(response.GetResponseStream())
use memoryStream = new MemoryStream(ASCIIEncoding.Default.GetBytes(reader.ReadToEnd()))
let jsonSerializer = DataContractJsonSerializer(typeof<List[]>)
let result = jsonSerializer.ReadObject(memoryStream) :?> List[]
Debug.WriteLine(sprintf "%A" result)
Actual Output - having nulls
[|
{Data = null;};
{Data = null;};
{Data = null;}
|]
Expected Output
[|
{Data = "/publication/a40a5e5c-98b3-45ae-d6a3-924b31d8712a/article/test;version=1521246543034";};
{Data = "/publication/a40a5e5c-98b3-45ae-d6a3-924b31d8712a/book/test2;version=1520623346891";}
{Data = "/publication/a40a5e5c-98b3-45ae-d6a3-924b31d8712a/catalog/test3;version=1520623346833";}
|]
Iteration
result
> Array.iter (fun x -> Console.WriteLine(x.Href))
I suspect there must be something wrong with how you're reading the data. I tried to reproduce this and replaced the reading from a stream with reading from a string - so that I can test this - and the following works fine (in F# interactive version 14.0.23413.0):
#r "System.Runtime.Serialization"
open System.IO
open System.Text
open System.Runtime.Serialization
open System.Runtime.Serialization.Json
[<DataContract>]
type List= {
[<field: DataMemberAttribute(Name="data") >]
Data: string
}
let reader = new StringReader("""[
{ "data": "/publication/a40a5e5c/article/test;version=1521246543034" },
{ "data": "/publication/a40a5e5c/book/test2;version=1520623346891" },
{ "data": "/publication/a40a5e5c/catalog/test3;version=1520623346833" } ]""")
let memoryStream = new MemoryStream(ASCIIEncoding.Default.GetBytes(reader.ReadToEnd()))
let jsonSerializer = DataContractJsonSerializer(typeof<List[]>)
let result = jsonSerializer.ReadObject(memoryStream) :?> List[]
result
Can you check that your input JSON is actually the one you shared here? To do that, see what reader.ReadToEnd() returns before calling GetBytes - I suspect there must be something wrong there, because the rest of the code works fine for me.
I using Objectmapper and Alamofire in my project.
Let's have a struct:
struct User: Mappable {
var name = ""
init?(map: Map) {}
mutating func mapping(map: Map) {
name <- map["name"]
}
}
and then i want to make a request to send array of users to server like this:
var users = [User]()
...
let parameters = ?//i want to convert users array to JSON
Alamofire.request("https://httpbin.org/post", method: .post, parameters: parameters).responseJSON {
...
}
But i don't know how to convert users array to parameters for input to Alamofire request.
To convert an object to String using mapper:
let user = User()
let userString = Mapper<User>.toJSONString(user)
To convert it to JSON:
let userJSON = Mapper<User>().toJSON(user)
You can checkout various apis provided by ObjectMapper by command-clicking 'Mapper' in your code or go to Mapper.swift.
I'm trying to use Object mapper https://github.com/Hearst-DD/ObjectMapper to convert a JSON string into a Swift object. Note I've simplified the object to a single field here - obviously my real response has more fields!
My response is:
data arrray response [{
chat = {
"_id" = 1;
}}]
So I want to convert to my chat class:
public class Chat: Mappable {
var _id: String? }
public required init?(map: Map){
}
public func mapping(map: Map) {
_id <- map["_id"]
}
}
So I convert my data array to a dictionary
let jsonResponse = dataArray [0]
let discResponse = jsonResponse as! Dictionary<String,AnyObject>
I can even access my field manually
let chat = discResponse["chat"]
let id = chat!["_id"]
print ("CHAT ID", id)
But mapping to the object
let jsonData = try! JSONSerialization.data(withJSONObject: chat, options: .prettyPrinted)
let user = Chat(JSONString: String( describing: jsonData))
returns nil
Why?
Just putting my comment as an answer, if someone will stuck on the same issue: use Mapper<Chat>().map(JSONObject: chat). It should help your cause.
I have a SQL Database on Azure and I would like to synchronize it with Realm, for my iOS App (in Swift)
For that, I have created a REST API which generates a JSON and now I would like to integrate this JSON in Realm.
To do that, I have tried to follow the explanation on Realm Documentation, so now I have :
Realm Table :
class tbl_test: Object {
dynamic var id:Int = 0
dynamic var name:String = ""
override class func primaryKey() -> String? {
return "id"
}
}
Swift Code :
let realm = try! Realm()
let stringTxt:String = "[{\"id\": 1, \"name\": \"My Name\"}]"
var myData = NSData()
if let dataFromString = stringTxt.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let jsonData = JSON(data: dataFromString)
if let encryptedData:NSData = try! jsonData.rawData() {
myData = encryptedData
}
}
try! realm.write {
let json = try! NSJSONSerialization.JSONObjectWithData(myData, options: NSJSONReadingOptions())
realm.create(tbl_test.self, value: json, update: true)
}
I use SwiftyJSON to convert my string to JSON.
When I run the program, I have this error message :
[__NSCFDictionary longLongValue]: unrecognized selector sent to
instance 0x7fdcc8785820 2016-07-06 10:25:30.090
mydrawing[9436:2732447] *** Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[__NSCFDictionary
longLongValue]: unrecognized selector sent to instance 0x7fdcc8785820'
Is it a good way to import JSON in Realm ? There is no official way according to what I have found, but this method should work...
The problem you're facing is that the structure of the data you're passing to Realm.create(_:value:update:) doesn't match what the method expects. It expects either a dictionary with keys corresponding to the managed properties on your model type, or an array with one element for each managed property.
After deserializing the JSON data, json looks like so:
(
{
id = 1;
name = "My Name";
}
)
That is an array containing a single element that is an dictionary. When you pass this array to Realm.create(_:value:update:), Realm expects the first element of the array to be the value to use as the id property on your tbl_test type.
I suspect that what you mean to do is to call Realm.create on each of the elements of the array in turn, instead of calling it on the array itself.