Rebuild JSON string to a different structure, parsing specific fields - json

I receive a JSON like this
{
"raw_content":"very long string"
"mode":"ML",
"user_id":"4000008367",
"user_description":"John Doe",
"model":3,
"dest_contact":"test#email.it",
"order_details":[
"ART.: 214883 PELL GRANI 9 ESPR.BAR SKGR 1000 SGOC.: 1000 GR\nVS.ART: 305920132 COMPOS. PALLET\n36 COLLI PEZ.RA: 6 TOT.PEZZI: 216 B: 12 T: 6\nEU C.L.: 24,230\nCO SCAP- : 16,500 CA SCAP- : 15,000 CO SCCP- : 0,000\nCO SAGV- : 0,00000\nC.N. : 17,200SCAD.MIN.: 25/01/22\nCONDIZIONI PAGAMENTO : 60GG B.B. DT RIC FT FINEMESE ART62\n",
"ART.: 287047 PELLINI BIO100%ARABICALTGR 250 SGOC.: 250 GR\nVS.ART: 315860176 COMPOS. PALLET\n36 COLLI PEZ.RA: 6 TOT.PEZZI: 216 B: 12 T: 3\nEU C.L.: 8,380\nCO SCAP- : 16,500 PR SCAP- : 15,000 CO SCCP- : 0,000\nCO SAGV- : 0,00000\nC.N. : 5,950SCAD.MIN.: 25/01/22\nCONDIZIONI PAGAMENTO : 60GG B.B. DT RIC FT FINEMESE ART62\n",
"ART.: 3137837 CAFFE PELLINI TOP LTGR 250 SGOC.: 250 GR\nVS.ART: 315850175 COMPOS. PALLET\n30 COLLI PEZ.RA: 12 TOT.PEZZI: 360 B: 6 T: 5\nEU C.L.: 6,810\nCO SCAP- : 16,500 PR SCAP- : 12,000 CO SCCP- : 0,000\nCO SAGV- : 0,00000\nC.N. : 5,000SCAD.MIN.: 18/08/21\nCONDIZIONI PAGAMENTO : 60GG B.B. DT RIC FT FINEMESE ART62\n",
"ART.: 7748220 ESPRES.SUP.TRADIZ. MOKPKGR 500 SGOC.: 500 GR\nVS.ART: 315930114 COMPOS. PALLET\n80 COLLI PEZ.RA: 10 TOT.PEZZI: 800 B: 10 T: 6\nEU C.L.: 7,580\nCO SCAP- : 16,500 PR SCAP- : 27,750 CO SCCP- : 0,000\nCO SAGV- : 0,00000\nC.N. : 4,570SCAD.MIN.: 25/01/22\nCONDIZIONI PAGAMENTO : 60GG B.B. DT RIC FT FINEMESE ART62\n"
],
"order_footer":"\nPALLET DA CM. 80X120\nT O T A L E C O L L I 182\n\n- EX D.P.R. 322 - 18/05/82 NON SI ACCETTANO TERMINI MINIMI DI\nCONSERVAZIONE INFERIORI A QUELLI INDICATI\n- CONSEGNA FRANCO BANCHINA, PALLET MONOPRODOTTO\nCOME DA PALLETTIZZAZIONE SPECIFICATA\nCONDIZIONI DI PAGAMENTO : COME DA ACCORDI\n"
}
and I want to reorder it to this
{
id: "32839ds8a32jjdas93193snkkk32jhds-k2j1", // generated, see my implementation
rawContent: "very long string",
parsedContent: {"mode":"ML", "user_id":"4000008367", "user_description":"John Doe", "order_details":[ "....." ], ... } // basically all the fields except raw content
}
How can I do this? I'm trying to work it with maps:
var output map[string]interface{}
var message processing.Document // message is of type struct {ID string, Raw string, Parsed string}
err = json.Unmarshal([]byte(doc), &output)
if err != nil {
// error handling
}
message.ParsedContent = "{"
for key, data := range output {
if key == "raw_content" {
hash := md5.Sum([]byte(data.(string)))
message.ID = hex.EncodeToString(hash[:])
message.RawContent = base64.RawStdEncoding.EncodeToString([]byte(data.(string)))
} else {
temp := fmt.Sprintf("\"%s\": \"%s\", ", key, data)
message.ParsedContent = message.ParsedContent + temp
}
}
message.ParsedContent = message.ParsedContent + "}"
msg, err := json.Marshal(message)
if err != nil {
// error handling
}
fmt.Println(string(msg))
There's a few problem with this. If it was only strings it would be ok, but there are integers and the sprintf doesn't work (the output I get, for example on the field "model" is "model": "%!s(float64=3)". I could do an if key == model and parse it as an int, but as I said the fields are not always the same and there are other integers that sometimes are there and sometimes are not there.
Also, the field "order_footer" has escaped new lines which are somehow deleted in my parsing, and this breaks the validity of the resulting JSON.
How can I solve this issues?
EDIT: As suggested, hand-parsing JSON is a bad idea. I could parse it into a struct, the field "model" actually tells me which struct to use. The struct for "model": 3 for example is:
type MOD3 struct {
Raw string `json:"raw_content"`
Mode string `json:"mode"`
UserID string `json:"user_id"`
UserDes string `json:"user_description"`
Model int `json:"model"`
Heading string `json:"legal_heading"`
DestContact string `json:"dest_contact"`
VendorID string `json:"vendor_id"`
VendorLegal string `json:"vendor_legal"`
OrderID string `json:"order_id"`
OrderDate int64 `json:"order_date"`
OrderReference string `json:"order_reference"`
DeliveryDate int64 `json:"delivery_date"`
OrderDestination string `json:"order_destination"`
OrderDestinationAddress string `json:"order_destination_address"`
Items []string `json:"order_details"`
OrderFooter string `json:"order_footer"`
}
At this point, how can I parse specific fields to the output format?

You should never, ever, ever try to generate JSON by hand. The steps should be:
Get JSON and parse it in a model object.
Create a copy of the model object with all the changes you want.
Convert the copied model object to JSON.
You don't know enough about JSON to modify it on the fly. I know enough, and the only reasonable way is complete parsing, and writing back the complete changes.

Related

Wants to generate json output from mysql using gorm in Go

So I am trying to get data from my database i.e MySQL. I am able to finish that step accessing the database but, the problem is I want to get the output in JSON format, but i did some research but didn't got the result so anyone can guide or hep me in this, getting the MySQL data in json by using GORM.
Here is the sample of my code which i written.
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql" //This must be introduced! !
)
type k_movie struct {
Id uint32
Title string `gorm:"default:''"`
Url_name string `gorm:"default:''"`
K_score string ``
Poster_url string ``
}
func main() {
db, errDb := gorm.Open("mysql", "root:xyz#123#(127.0.0.1)/dbdump?charset=utf8mb4&loc=Local")
if errDb != nil {
fmt.Println(errDb)
}
defer db.Close() //Close the database connection after use up
db.LogMode(true) //Open sql debug mode
//SELECT * FROM `k_movies` WHERE (id>0 and id<.....)
var movies []k_movie
db.Where("id>? and id<?", 0, 103697).Limit(3).Find(&movies)
fmt.Println(movies)
//Get the number
total := 0
db.Model(&k_movie{}).Count(&total)
fmt.Println(total)
var infos []k_movie //Define an array to receive multiple results
db.Where("Id in (?)", []uint32{1, 2, 3, 4, 5, 6, 7, 8}).Find(&infos)
fmt.Println(infos)
fmt.Println(len(infos)) //Number of results
var notValue []k_movie
db.Where("id=?", 3).Find(&notValue)
if len(notValue) == 0 {
fmt.Println("No data found!")
} else {
fmt.Println(notValue)
}
}
And the output I'm getting in this format.
kumardivyanshu#Divyanshus-MacBook-Air ~/myproject/src/github.com/gorm_mysql % go run test.go
(/Users/kumardivyanshu/myproject/src/github.com/gorm_mysql/test.go:31)
[2021-05-13 08:59:45] [3.89ms] SELECT * FROM `k_movies` WHERE (id>0 and id<103697) LIMIT 3
[3 rows affected or returned ]
[{1 Golmaal: Fun Unlimited golmaal-fun-unlimited 847 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/32b7385e1e616d7ba3d11e1bee255ecce638a136} {2 Dabangg 2 dabangg-2 425 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/1420c4d6f817d2b923cd8b55c81bdb9d9fd1eca0} {3 Force force 519 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/cd1dc247da9d16e194f4bfb09d99f4dedfb2de00}]
(/Users/kumardivyanshu/myproject/src/github.com/gorm_mysql/test.go:36)
[2021-05-13 08:59:45] [20.22ms] SELECT count(*) FROM `k_movies`
[0 rows affected or returned ]
103697
(/Users/kumardivyanshu/myproject/src/github.com/gorm_mysql/test.go:40)
[2021-05-13 08:59:45] [2.32ms] SELECT * FROM `k_movies` WHERE (Id in (1,2,3,4,5,6,7,8))
[8 rows affected or returned ]
[{1 Golmaal: Fun Unlimited golmaal-fun-unlimited 847 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/32b7385e1e616d7ba3d11e1bee255ecce638a136} {2 Dabangg 2 dabangg-2 425 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/1420c4d6f817d2b923cd8b55c81bdb9d9fd1eca0} {3 Force force 519 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/cd1dc247da9d16e194f4bfb09d99f4dedfb2de00} {4 Eega eega 906 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/08aef7d961d4699bf2d12a7c854b6b32d1445247} {5 Fukrey fukrey 672 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/5d14bd2fb0166f4bb9ab919e31b69f2605f366aa} {6 London Paris New York london-paris-new-york 323 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/222d8a6b5c76b1d3cfa0b93d4bcf1a1f16f5e199} {7 Bhaag Milkha Bhaag bhaag-milkha-bhaag 963 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/efa8b86c753ae0110cc3e82006fadabb06f1486c} {8 Bobby Jasoos bobby-jasoos 244 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/0e9540d4c962ec33d8b63c0c563e7b64169122e0}]
8
(/Users/kumardivyanshu/myproject/src/github.com/gorm_mysql/test.go:45)
[2021-05-13 08:59:45] [1.56ms] SELECT * FROM `k_movies` WHERE (id=3)
[1 rows affected or returned ]
[{3 Force force 519 https://movieassetsdigital.sgp1.cdn.digitaloceanspaces.com/thumb/cd1dc247da9d16e194f4bfb09d99f4dedfb2de00}]
You need to define the json tag on your struct, so you can use the json.Marshal to grab a []byte slice that presents a json object.
Example taken from Go by example:
type Response2 struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
}
res2D := &Response2{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}}
res2B, _ := json.Marshal(res2D)
fmt.Println(string(res2B))
That would print:
{"page":1,"fruits":["apple","peach","pear"]}

JSON struct to csv in Go Lang

Looking for ideas to export JSON read structs into some csv format while preserving the hierarchy.
https://play.golang.org/p/jf2DRL1hC5K
/*
Expected output in excel for data wrangling:
A Key | B Key | C Key | D Key
SomethingA SomethingB SomethingC SomethingF
SomethingA SomethingB SomethingC SomethingG
SomethingA SomethingB SomethingC [1,2,3]
*/
I have tried iterating through the struct as following
for _, value := range mymodel {
fmt.Println(value)
/* could not iterate over structs */
}
Adding a method to your RootModel for both header and individual rows (so you can range over the type and only print the header once):
type RootModel struct {
A string
B string
C string
D factors
}
type factors struct {
F string
G string
H []int
}
func (*RootModel) CSVheader(w io.Writer) {
cw := csv.NewWriter(w)
cw.Write([]string{"A Key", "B Key", "C Key", "D Key"})
cw.Flush()
}
func (rm *RootModel) CSVrow(w io.Writer) {
cw := csv.NewWriter(w)
cw.Write([]string{rm.A, rm.B, rm.C, rm.D.F})
cw.Write([]string{rm.A, rm.B, rm.C, rm.D.G})
is, _ := json.Marshal(rm.D.H)
cw.Write([]string{rm.A, rm.B, rm.C, string(is)})
cw.Flush()
}
Playground: https://play.golang.org/p/c8UQVQ8tQTX
Output:
A Key,B Key,C Key,D Key
SomethingA,SomethingB,SomethingC,SomethingF
SomethingA,SomethingB,SomethingC,SomethingG
SomethingA,SomethingB,SomethingC,"[1,2,3]"
Note: if you are dealing with a slice of RootModel, you may want to put the CSV writer logic at that level - so it can handle the single render of the header-row and then the subsequent data rows.

Decoding polymorphic JSON objects into elm with andThen

My JSON looks similar to this:
{ "items" :
[ { "type" : 0, "order": 10, "content": { "a" : 10, "b" : "description", ... } }
, { "type" : 1, "order": 11, "content": { "a" : 11, "b" : "same key, but different use", ... } }
, { "type" : 2, "order": 12, "content": { "c": "totally different fields", ... } }
...
]
}
and I want to use the type value to decide what union type to create while decoding. So, I defined alias types and decoders for all the above in elm :
import Json.Decode exposing (..)
import Json.Decode.Pipeline exposing (..)
type alias Type0Content = { a : Int, b : String }
type alias Type1Content = { a : Int, b2 : String }
type alias Type2Content = { c : String }
type Content = Type0 Type0Content | Type1 Type1Content | Type2 Type2Content
type alias Item = { order : Int, type : Int, content: Content }
decode0 = succeed Type0Content
|> requiredAt ["content", "a"] int
|> requiredAt ["content", "b"] string
decode1 = succeed Type1Content
|> requiredAt ["content", "a"] int
|> requiredAt ["content", "b"] string
decode2 = succeed Type2Content
|> requiredAt ["content", "c"] string
decodeContentByType hint =
case hint of
0 -> Type0 decode0
1 -> Type1 decode1
2 -> Type2 decode2
_ -> fail "unknown type"
decodeItem = succeed Item
|> required "order" int
|> required "type" int `andThen` decodeContentByType
Can't get the last two functions to interact as needed.
I've read through page 33 of json-survival-kit by Brian Thicks, but that didn't bring me on track either.
Any advice and lecture appreciated!
It looks like the book was written targeting Elm 0.17 or below. In Elm 0.18, the backtick syntax was removed. You will also need to use a different field name for type since it is a reserved word, so I'll rename it type_.
Some annotations might help narrow down bugs. Let's annotate decodeContentByType, because right now, the branches aren't returning the same type. The three successful values should be mapping the decoder onto the expected Content constructor:
decodeContentByType : Int -> Decoder Content
decodeContentByType hint =
case hint of
0 -> map Type0 decode0
1 -> map Type1 decode1
2 -> map Type2 decode2
_ -> fail "unknown type"
Now, to address the decodeItem function. We need three fields to satisfy the Item constructor. The second field is the type, which can be obtained via required "type" int, but the third field relies on the "type" value to deduce the correct constructor. We can use andThen (with pipeline syntax as of Elm 0.18) after fetching the Decoder Int value using Elm's field decoder:
decodeItem : Decoder Item
decodeItem = succeed Item
|> required "order" int
|> required "type" int
|> custom (field "type" int |> andThen decodeContentByType)

Using Microsoft.FSharpLu to serialize JSON to a stream

I've been using the Newtonsoft.Json and Newtonsoft.Json.Fsharp libraries to create a new JSON serializer and stream to a file. I like the ability to stream to a file because I'm handling large files and, prior to streaming, often ran into memory issues.
I stream with a simple fx:
open Newtonsoft.Json
open Newtonsoft.Json.FSharp
open System.IO
let writeToJson (path: string) (obj: 'a) : unit =
let serialized = JsonConvert.SerializeObject(obj)
let fileStream = new StreamWriter(path)
let serializer = new JsonSerializer()
serializer.Serialize(fileStream, obj)
fileStream.Close()
This works great. My problem is that the JSON string is then absolutely cluttered with stuff I don't need. For example,
let m =
[
(1.0M, None)
(2.0M, Some 3.0M)
(4.0M, None)
]
let makeType (tup: decimal * decimal option) = {FieldA = fst tup; FieldB = snd tup}
let y = List.map makeType m
Default.serialize y
val it : string =
"[{"FieldA": 1.0},
{"FieldA": 2.0,
"FieldB": {
"Case": "Some",
"Fields": [3.0]
}},
{"FieldA": 4.0}]"
If this is written to a JSON and read into R, there are nested dataframes and any of the Fields associated with a Case end up being a list:
library(jsonlite)
library(dplyr)
q <- fromJSON("default.json")
x <-
q %>%
flatten()
x
> x
FieldA FieldB.Case FieldB.Fields
1 1 <NA> NULL
2 2 Some 3
3 4 <NA> NULL
> sapply(x, class)
FieldA FieldB.Case FieldB.Fields
"numeric" "character" "list"
I don't want to have to handle these things in R. I can do it but it's annoying and, if there are files with many, many columns, it's silly.
This morning, I started looking at the Microsoft.FSharpLu.Json documentation. This library has a Compact.serialize function. Quick tests suggest that this library will eliminate the need for nested dataframes and the lists associated with any Case and Field columns. For example:
Compact.serialize y
val it : string =
"[{
"FieldA": 1.0
},
{
"FieldA": 2.0,
"FieldB": 3.0
},
{
"FieldA": 4.0
}
]"
When this string is read into R,
q <- fromJSON("compact.json")
x <- q
x
> x
FieldA FieldB
1 1 NA
2 2 3
3 4 NA
> sapply(x, class)
FieldA FieldB
"numeric" "numeric
This is much simpler to handle in R. and I'd like to start using this library.
However, I don't know if I can get the Compact serializer to serialize to a stream. I see .serializeToFile, .desrializeStream, and .tryDeserializeStream, but nothing that can serialize to a stream. Does anyone know if Compact can handle writing to a stream? How can I make that work?
The helper to serialize to stream is missing from the Compact module in FSharpLu.Json, but you should be able to do it by following the C# example from
http://www.newtonsoft.com/json/help/html/SerializingJSON.htm. Something along the lines:
let writeToJson (path: string) (obj: 'a) : unit =
let serializer = new JsonSerializer()
serializer.Converters.Add(new Microsoft.FSharpLu.Json.CompactUnionJsonConverter())
use sw = new StreamWriter(path)
use writer = new JsonTextWriter(sw)
serializer.Serialize(writer, obj)

How can I convert dictionary in list to dictionary in Python 3?

import urllib.request as request
import json
api = "https://kr.api.pvp.net/championmastery/location/KR/player/38281748/topchampions?api_key=RGAPI-6bdee369-a91d-485a-9280-444de0e37afe"
api_data = request.urlopen(api).read().decode("utf-8")
apiload = json.loads(api_data)
print(apiload)
I want to print my League of Legends champion Points.
So I use https://developer.riotgames.com/api/methods#!/1091/3768 this API, and
convert to Python object. but this API's Return Value is List[ChampionMasteryDTO],
which means I can't use it as dictionary.
apiload contain [{"key" : "value"}, ... {"key" : "value"}]
how can I make apiload as dictionary?
The apiload variable prints two dictionaries within a list.
If you would like to create a new dictionary using the apiload you can do the following:
#create a new dictionary
my_dict = {}
#now iterate through the list
for item in apiload:
#now iterate through the dictionaries that are in the list:
for key, value in item.items():
#assign the key value to the new declared dictionary
new_dict[key] = value
This will create a new dictionary with the following output:
championId : 91
tokensEarned : 0
championPointsSinceLastLevel : 339079
chestGranted : True
lastPlayTime : 1478451844000
playerId : 38281748
championLevel : 7
championPoints : 360679
championPointsUntilNextLevel : 0
championId : 5
tokensEarned : 0
championPointsSinceLastLevel : 129110
chestGranted : True
lastPlayTime : 1478454752000
playerId : 38281748
championLevel : 7
championPoints : 150710
championPointsUntilNextLevel : 0
championId : 21
tokensEarned : 0
championPointsSinceLastLevel : 2018
chestGranted : False
lastPlayTime : 1476197348000
playerId : 38281748
championLevel : 4
championPoints : 14618
championPointsUntilNextLevel : 6982
Hope that helps.
Regarding the structure of your response, the easy one if you just want to print your points, would be to just use a loop actually :
for el in apiload:
print(el["championPoints"])
If your problem is to print the total of points collected, use a collections.Counter :
from collections import Counter
cnt = Counter()
for col in apiload:
for k in col.keys():
cnt[k] += c.get(k, 0)
print(cnt['championPoints']) # should print the sum of championPoints