clbanning / mxj Goto Github PK
View Code? Open in Web Editor NEWDecode / encode XML to/from map[string]interface{} (or JSON); extract values with dot-notation paths and wildcards. Replaces x2j and j2x packages.
License: MIT License
Decode / encode XML to/from map[string]interface{} (or JSON); extract values with dot-notation paths and wildcards. Replaces x2j and j2x packages.
License: MIT License
I have the following minimal example:
// main.go
package main
import (
"net/http"
"strings"
"github.com/clbanning/mxj"
)
func main() {
request, _ := http.NewRequest("POST", "/endpoint", strings.NewReader("not an XML"))
_, _ = mxj.NewMapXmlReader(request.Body)
}
Running it (go run main.go
) gives fatal error: runtime: out of memory
after some seconds of freezing.
Full output:
fatal error: runtime: out of memory
runtime stack:
runtime.throw(0x6460b0, 0x16)
/usr/local/go/src/runtime/panic.go:566 +0x95
runtime.sysMap(0xc4a2210000, 0x82000000, 0x0, 0x769fd8)
/usr/local/go/src/runtime/mem_linux.go:219 +0x1d0
runtime.(*mheap).sysAlloc(0x750da0, 0x82000000, 0x40dc00)
/usr/local/go/src/runtime/malloc.go:407 +0x37a
runtime.(*mheap).grow(0x750da0, 0x41000, 0x0)
/usr/local/go/src/runtime/mheap.go:726 +0x62
runtime.(*mheap).allocSpanLocked(0x750da0, 0x41000, 0xc4200001a0)
/usr/local/go/src/runtime/mheap.go:630 +0x4f2
runtime.(*mheap).alloc_m(0x750da0, 0x41000, 0x7f0100000000, 0x7fe848aece10)
/usr/local/go/src/runtime/mheap.go:515 +0xe0
runtime.(*mheap).alloc.func1()
/usr/local/go/src/runtime/mheap.go:579 +0x4b
runtime.systemstack(0x7fe848aece18)
/usr/local/go/src/runtime/asm_amd64.s:314 +0xab
runtime.(*mheap).alloc(0x750da0, 0x41000, 0x10100000000, 0xc420010ff0)
/usr/local/go/src/runtime/mheap.go:580 +0x73
runtime.largeAlloc(0x81ffffff, 0xc420010f01, 0xc42004f120)
/usr/local/go/src/runtime/malloc.go:774 +0x93
runtime.mallocgc.func1()
/usr/local/go/src/runtime/malloc.go:669 +0x3e
runtime.systemstack(0xc420020a00)
/usr/local/go/src/runtime/asm_amd64.s:298 +0x79
runtime.mstart()
/usr/local/go/src/runtime/proc.go:1079
goroutine 1 [running]:
runtime.systemstack_switch()
/usr/local/go/src/runtime/asm_amd64.s:252 fp=0xc42004f028 sp=0xc42004f020
runtime.mallocgc(0x81ffffff, 0x5ef6e0, 0xc42004f101, 0x44fb40)
/usr/local/go/src/runtime/malloc.go:670 +0x903 fp=0xc42004f0c8 sp=0xc42004f028
runtime.makeslice(0x5ef6e0, 0x81ffffff, 0x81ffffff, 0x72d880, 0xc42000c150, 0xc42004f190)
/usr/local/go/src/runtime/slice.go:57 +0x7b fp=0xc42004f120 sp=0xc42004f0c8
bytes.makeSlice(0x81ffffff, 0x0, 0x0, 0x0)
/usr/local/go/src/bytes/buffer.go:198 +0x77 fp=0xc42004f160 sp=0xc42004f120
bytes.(*Buffer).grow(0xc42008c730, 0x1, 0x4c00000040fffffe)
/usr/local/go/src/bytes/buffer.go:106 +0x178 fp=0xc42004f1b0 sp=0xc42004f160
bytes.(*Buffer).WriteByte(0xc42008c730, 0x14c, 0x0, 0x0)
/usr/local/go/src/bytes/buffer.go:235 +0x3c fp=0xc42004f1d8 sp=0xc42004f1b0
encoding/xml.(*Decoder).text(0xc42008c6e0, 0xffffffffffffffff, 0x0, 0x0, 0x0, 0x0)
/usr/local/go/src/encoding/xml/xml.go:1076 +0x106 fp=0xc42004f388 sp=0xc42004f1d8
encoding/xml.(*Decoder).rawToken(0xc42008c6e0, 0xc42004fb10, 0x4649a4, 0x600780, 0xc4200109c0)
/usr/local/go/src/encoding/xml/xml.go:525 +0x2b43 fp=0xc42004f778 sp=0xc42004f388
encoding/xml.(*Decoder).Token(0xc42008c6e0, 0xc420010a20, 0x7, 0xc42004fac0, 0x408334)
/usr/local/go/src/encoding/xml/xml.go:249 +0x1484 fp=0xc42004f9c0 sp=0xc42004f778
github.com/clbanning/mxj.xmlToMapParser(0x0, 0x0, 0x0, 0x0, 0x0, 0xc42008c6e0, 0x0, 0x5caa11, 0x18, 0x28)
/home/eugzol/Projects/go/src/github.com/clbanning/mxj/xml.go:311 +0xa5 fp=0xc42004fda0 sp=0xc42004f9c0
github.com/clbanning/mxj.xmlReaderToMap(0x72d8c0, 0xc420010f60, 0x0, 0xc42000cfdf, 0x1, 0x1)
/home/eugzol/Projects/go/src/github.com/clbanning/mxj/xml.go:140 +0x240 fp=0xc42004fe80 sp=0xc42004fda0
github.com/clbanning/mxj.NewMapXmlReader(0x7fe84aa4e058, 0xc42000d090, 0x0, 0x0, 0x0, 0xc4200cc820, 0xc4200d00f0, 0x0)
/home/eugzol/Projects/go/src/github.com/clbanning/mxj/xml.go:86 +0x96 fp=0xc42004fee8 sp=0xc42004fe80
main.main()
/home/eugzol/Projects/go/src/github.com/MyceliumGear/test/main.go:12 +0x104 fp=0xc42004ff48 sp=0xc42004fee8
runtime.main()
/usr/local/go/src/runtime/proc.go:183 +0x1f4 fp=0xc42004ffa0 sp=0xc42004ff48
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:2086 +0x1 fp=0xc42004ffa8 sp=0xc42004ffa0
goroutine 17 [syscall, locked to thread]:
runtime.goexit()
/usr/local/go/src/runtime/asm_amd64.s:2086 +0x1
exit status 2
Go version: go version go1.7.1 linux/amd64
Related to issue #28, what I am looking forward to in mxj, is able to locate some records according to XPath/JPath or something similar, them modify the located record.
Please take a look at this .xml file for example, I need to, expressed in xpath term,
//Request
nodes; and for each node found,For example, these changes were to change
//Request
's ./[ReportingName]
use the basename
of ./[Url]
( and prefix with ../TransactionTimer[Name]
as well, but that's a bonus request if it is too difficult at first).
The final result is posted here.
How can I do that? Thx.
Package github.com/clbanning/mxj/j2x uses m.XmlWriterRaw removed on v1.8.4
github.com/clbanning/mxj/j2x
../clbanning/mxj/j2x/j2x.go:39:2: not enough arguments to return
../clbanning/mxj/j2x/j2x.go:39:10: m.XmlWriterRaw undefined (type mxj.Map has no field or method XmlWriterRaw)
../clbanning/mxj/j2x/j2x.go:58:17: m.XmlWriterRaw undefined (type mxj.Map has no field or method XmlWriterRaw)
https://github.com/clbanning/mxj/blob/master/j2x/j2x.go#L34-L40
https://github.com/clbanning/mxj/blob/master/j2x/j2x.go#L53-L60
There seems to be a couple issues with this function.
Getting a runtime error: index out of range [recovered]
First the switch is on the length of the split string. If the length of the string array is 3 (case 3), then you can't reference vv[3] which would be the 4th element right? It must be vv[2] to access the third string.
Second, if your text has any ":" in it, it will get confused. My text had a "http://blah/blah/blah" in it. So, after I made the above change, I got the error: "unknown subkey conversion spec:..."
Basically I was trying to read in a kml file, change the description and name fields and then print it back out. I was trying to use the
paths= PathsForKey("description")
values =ValuesForPath(paths[0]).
UpdateValuesForPath(newVal, path[0], "description:"+value[0])
with a double loop in there to get all the paths and values. But, because description can have any kind of text, this will not be possible. Is there a better way of doing this?
Thanks,
Lorie
This is a great library! Thanks for making this. When converting XML to JSON should we be using a Hypen for attributes? Why not use an underscore? The reason being is that this library is highly useful for server side processing where it concerns Javascript...so turning that JSON into a POJO would be better aligned using an underscore...unfortunately the $ sign is way to overused.
Is there any way to get the ordering of inner tags, if they are variable in their tag name?
For example:
Obj
Attr3
Attr1
Attr2
Obj
This ends up a map
{Obj:{Attr1:"",Attr2:"",Attr3:""}}, and there is no way to tell that the order was 3,1,2. I've run into a situation where I need to know the order, is there some flag or something that would allow me to turn the attributes into an ordered array, or to write out the order the parser ran into them as an attribute or something?
Hi,
Many thanks for writing and especially for maintaining the package.
When running NewMapXml() with an XML containing 2 or more items it results an []interface{}
of the items, otherwise only the item (mostly map[string]interface{}), see below:
package main
import (
"fmt"
"io"
"reflect"
"github.com/clbanning/mxj"
)
func main() {
type Map map[string]interface{}
x := []byte(`<doc>
<book>
<author>A</author>
</book>
<book>
<author>B</author>
</book>
</doc>`)
y := []byte(`<doc>
<book>
<author>A</author>
</book>
</doc>`)
m, err := mxj.NewMapXml(x)
if err != nil && err != io.EOF {
panic(err)
}
n, err := mxj.NewMapXml(y)
if err != nil && err != io.EOF {
panic(err)
}
m1 := reflect.ValueOf(m["doc"])
n1 := reflect.ValueOf(n["doc"])
if m1.MapIndex(m1.MapKeys()[0]) != n1.MapIndex(n1.MapKeys()[0]) {
fmt.Printf("%#v != %#v", m1, n1)
}
}
Tested it with go 1.10.1 and 1.7.0
We currently have a use case where we take a large XML payload and convert it to JSON for manipulation and then convert it back to XML. An example payload:
<Message source="RDC" target="IDC" type="CWCUSTHISTOUT">
<Headers>
<Header company_code="1" order_id="123" reference_order_number="fff123" customer_number="123" order_date="09202016" order_channel="E" bill_me_later_ind="N">
<ShipTos><ShipTo ship_to_number="1" ship_to_status="X" gift_order="N" ship_via_code="1" ship_via_description="FEDEX HOME" shipping_override="N"/></ShipTos>
</Header>
<Header company_code="1" order_id="123" customer_number="123" order_date="05092013" bill_me_later_ind="N">
<ShipTos><ShipTo ship_to_number="1" sub_total="138.50" shipping="11.95" tax="13.55" order_total="164.00" ship_to_status="X" gift_order="N" ship_via_code="1" ship_via_description="FEDEX HOME" shipping_override="N"/></ShipTos>
</Header>
</Headers>
</Message>
The resulting JSON is fine:
{
"Message": {
"-source": "RDC",
"-target": "IDC",
"-type": "CWCUSTHISTOUT",
"Headers": {
"Header": [{
"-bill_me_later_ind": "N",
"-company_code": "1",
"-customer_number": "123",
"-order_channel": "E",
"-order_date": "09202016",
"-order_id": "123",
"-reference_order_number": "fff123",
"ShipTos": {
"ShipTo": {
"-gift_order": "N",
"-ship_to_number": "1",
"-ship_to_status": "X",
"-ship_via_code": "1",
"-ship_via_description": "FEDEX HOME",
"-shipping_override": "N"
}
}
}, {
"-bill_me_later_ind": "N",
"-company_code": "1",
"-customer_number": "123",
"-order_date": "05092013",
"-order_id": "123",
"ShipTos": {
"ShipTo": {
"-gift_order": "N",
"-order_total": "164.00",
"-ship_to_number": "1",
"-ship_to_status": "X",
"-ship_via_code": "1",
"-ship_via_description": "FEDEX HOME",
"-shipping": "11.95",
"-shipping_override": "N",
"-sub_total": "138.50",
"-tax": "13.55"
}
}
}]
}
}
}
The issue occurs when parsing it back to XML:
<Message source="RDC" target="IDC" type="CWCUSTHISTOUT"><Headers>>UNKNOWN/></Headers></Message>
With a little digging the following error is being generated here:
xml: unsupported type: map[string]interface {}
Hello.
I'm trying to use library to parse XMPP stream.
This is can be done quite simple with HandleXmlReader
and NewXmlMapReader
but since everything is going after <stream:stream>
until it'll be closed parser just got stuck.
If it's not possible to handle continuous streams I just want to skip starting tag and continue from next one. So at least passing startElement
to begin parse with next token will be a fine solution.
Input json:
"ipv4": { "address-mode": "manual", "address": { "ip": "10.10.10.10", "prefix-length": 30 }
Output xml:
<ipv4><address><ip>10.0.22.14</ip><prefix-length>30.000000</prefix-length></address><address-mode>manual</address-mode><enabled>true</enabled><mtu>1500.000000</mtu><vti_peer>10.10.10.10</vti_peer></ipv4>
In this case, prefix-length:30 became 30.000000
Do anyone have the same problem with me?
Here're the codes using version v1.8.4
of mxj
:
package main
import (
"fmt"
"github.com/clbanning/mxj"
)
func main() {
type Cdata string
data, err := mxj.Map(map[string]interface{}{
"data": Cdata("test"),
}).Xml()
if err != nil {
panic(err)
}
fmt.Println(string(data))
}
And this outputs:
<Cdata>test</Cdata></data>
There's a invalid </data>
tag in the result. What I really expects is:
<data>test</data>
Do I miss somthing?
I have this:
mxj.XMLEscapeChars(true)
final, err := newImport.XmlSeq()
_, err = io.WriteString(fo, string(final))
The output file shows: <shortDescription>>0-2y</shortDescription>
The original source is: <shortDescription>>0-2y</shortDescription>
and that is the way I want it output. Am I missing something?
Hi !
When I try to test your project, it fails in the current directory:
command: go test -buildmode pie -compiler gc -ldflags "-X github.com/clbanning/mxj/version=1.8.2 -extldflags '-Wl,-z,relro '"
testing: github.com/clbanning/mxj
----------- TestXMPPStreamTagSeq ...
<stream:stream
from='example.com'
xmlns="jabber:client"
xmlns:stream="http://etherx.jabber.org/streams"
version="1.0">
map[stream:stream:map[#attr:map[from:map[#seq:0 #text:example.com] xmlns:map[#seq:1 #text:jabber:client] xmlns:stream:map[#text:http://etherx.jabber.org/streams #seq:2] version:map[#text:1.0 #seq:3]]]]
stream:features
</stream:features>
map[stream:features:map[sm:map[#attr:map[xmlns:map[#seq:0 #text:urn:xmpp:sm:3]] #seq:1] bind:map[#attr:map[xmlns:map[#text:urn:ietf:params:xml:ns:xmpp-bind #seq:0]] #seq:0]]]
stream:stream
map[stream:stream:map[]]
--- FAIL: ExampleMap_Struct (0.00s)
got:
mapVal: mxj.Map{"private":"Somewhere over the rainbow", "int":4, "str":"now's the time", "float":3.14159, "bool":true}
strVal: mxj_test.str{IntVal:4, StrVal:"now's the time", FloatVal:3.14159, BoolVal:true, private:""}
want (unordered):
mapVal: mxj.Map{"int":4, "str":"now's the time", "float":3.14159, "bool":true, "private":"Somewhere over the rainbow"}
strVal: mxj_test.str{IntVal:4, StrVal:"now's the time", FloatVal:3.14159, BoolVal:true, private:""}
Thanks
I'm trying to parse an Apache Tomcat server.xml file, make updates and save it back to a file (in exactly the same structure & layout). I've been using NewMapXmlSeq followed by SetValueForPath and then XmlSeqIndent. Broadly speaking it works, updating the value as desired but when the content is resaved a large amount of the comment content is gone. Debugging the issue it looks like the content is lost at the time of import. I've attached a file to test with but the start of the file up until the root element (<Server...) will need to be stripped before the file is parsed (to avoid the no root issue). I've not been able to identify that I'm doing anything wrong in the processing but happy to hear if that is the case. Thanks.
server_xml.zip
Hi @clbanning , I have xml document below and got error xml.Decoder.Token() - XML syntax error on line 2: invalid character entity & (no semicolon)
when parsing xml to map:
<document> <name>Bill & Hallett</name> <salute>Duc & 123xx</salute> <goes_by/> <lang>E</lang> </document>
Just read your code, and found problem at xmlToMapParser
function. You're using xml.Decoder
with Strict=true
by default that dose not allow special characters (https://golang.org/pkg/encoding/xml/#Decoder)
Could you fix it or expose some ways to enable/disable Strict
mode from xm.Decoder object?
Is it possible to select an XML node using one of the ValueFor...
methods and then work with it as with root node?
I've tried following:
mv, err := mxj.NewMapXml([]byte(sample_xml))
if err != nil {
log.Printf("Error parsing XML data: %v\n", err)
} else {
tst, _ := mv.ValueForKey("some-tag")
if mv2, ok := tst.(mxj.Map); ok {
xmlStr, _ := mv2.XmlIndent("====", " ")
log.Printf("Pretty XML:\n")
log.Printf("%s\n", xmlStr)
} else {
log.Printf("Error: Cant convert tst to map[string]interface{}\n")
}
}
But getting the Error: Cant convert tst to map[string]interface{}
output.
Probably missing something obvious from the docs here...Any pointers as to what?
I got the following example
package main
import (
"github.com/clbanning/mxj"
"encoding/json"
"fmt"
"bytes"
)
func main() {
var data = `{"lastname":"Mustermann","phone": null}"`
mxj.JsonUseNumber = true
var x interface{}
dec := json.NewDecoder(bytes.NewReader([]byte(data)))
dec.UseNumber()
if err := dec.Decode(&x); err != nil {
fmt.Printf("Unmarshal: %v", err)
}
xx, err := mxj.AnyXmlIndent(x, "", " ", "body")
if err != nil {
fmt.Println(err)
}
fmt.Println(string(xx))
}
Expected result is an empty phone element
<body>
<lastname>Mustermann</lastname>
<phone/>
</body>
This is working in commit 1db1a88
but broken from commit adfeeb9
From this commit up to current master, we got this result
<body>
<lastname>Mustermann</lastname>
<phone
</body>
Hi there, mxj is a great project and I've been using it in my gf project in XML marshal/unmarshal. It worked quite well with map[string]interface{}
type, but when the parameter contains other map type (eg: map[string]string
/map[string]int
etc...), it failed working.
Here's the simple codes to reproduce this issue:
package main
import (
"fmt"
"github.com/clbanning/mxj"
)
func main() {
m := make(map[string]interface{})
m["m"] = map[string]string {
"k" : "v",
}
b, _ := mxj.Map(m).Xml()
fmt.Println(string(b))
}
It's supposed to give me {"m":{"k":"v"}}
, but actually I got >UNKNOWN/>
.
Would you please take a look, and kindly give me a nice feedback, thanks!
Existing x2j users like
https://github.com/hudl/fargo/blob/master/metadata.go#L50
expect x2j.ByteDocToMap to exist
// XML: wrap in a BS xml tag so all metadata tags are pulled
fullDoc := append(append([]byte("<d>"), im.Raw...), []byte("</d>")...)
parsedDoc, err := x2j.ByteDocToMap(fullDoc, true)
if err != nil {
log.Errorf("Error unmarshalling: %s", err.Error())
return fmt.Errorf("error unmarshalling: %s", err.Error())
}
im.parsed = parsedDoc["d"].(map[string]interface{})
Inherited a project and saw that x2j (https://github.com/clbanning/x2j) migrated to this mxj -
However "DocToMap(data)" isn't here in mxj. Is there a function that replaces that?
I use the following XML for umarshaling it into a MapSeq. If there is a XML declaration (<?xml version="1.0" encoding="UTF-8"?>
) the NewMapXmlSeq
function returns the no root key
error.
What made me wonder, is that if I use the exact same XML with NewMapXml all works without problems.
Any idea what could be wrong here?
Used XML:
<?xml version="1.0" encoding="UTF-8"?>
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
<responseDate>2019-08-06T14:09:14Z</responseDate>
</OAI-PMH>
I'm trying to manipulate an XML file similar to the following example (simplified):
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="http://example.com" catalog-id="my-catalog">
<product product-id="a">
<online-flag>false</online-flag>
<available-flag>true</available-flag>
</product>
<product product-id="b">
<online-flag>true</online-flag>
<available-flag>true</available-flag>
</product>
</catalog>
Essentially I want to remove any <product>
where <online-flag>
is set to false and output a new XML file reflecting those changes. Here's the code I'm using to try to accomplish that:
package main
import (
"log"
"os"
"strconv"
"github.com/clbanning/mxj"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
catalogXML, err := os.Open("catalog_example.xml")
check(err)
defer catalogXML.Close()
mv, err := mxj.NewMapXmlReader(catalogXML)
check(err)
item, err := mv.ValuesForPath("catalog.product")
check(err)
total := 0
online := 0
for i, v := range item {
data := v.(map[string]interface{})
if data["online-flag"] == "true" {
online++
} else {
path := "catalog.product[" + strconv.Itoa(i) + "]"
log.Println("removing", path)
getpid, err := mv.ValueForPath(path)
check(err)
log.Println(getpid)
err = mv.Remove(path)
check(err)
}
total++
}
catalogXMLReduced, err := os.Create("catalog_reduced.xml")
check(err)
defer catalogXMLReduced.Close()
err = mv.XmlWriter(catalogXMLReduced)
check(err)
log.Println(online, "online /", total, "total")
}
Here's the output:
2020/09/01 00:43:33 removing catalog.product[0]
2020/09/01 00:43:33 map[-product-id:a available-flag:true online-flag:false]
panic: prevValueByPath: didn't find path – product[0]
goroutine 1 [running]:
main.check(...)
main.go:13
main.main()
main.go:44 +0x630
It would appear as though the path notation I'm using to reference the specific product key is supported in ValueForPath(), but throws an error when used with Remove().
the follwing error happens with go get on projects that include clbanning/mxj
github.com/clbanning/mxj/examples/gonuts9.go:8:2: cannot find package "tamgroup/mxj"
also:
github.com/clbanning/mxj/examples
github.com/clbanning/mxj/examples/getmetrics1.go:47: main redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/books.go:36
github.com/clbanning/mxj/examples/getmetrics2.go:48: main redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/getmetrics1.go:47
github.com/clbanning/mxj/examples/getmetrics3.go:49: main redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/getmetrics2.go:48
github.com/clbanning/mxj/examples/getmetrics4.go:50: main redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/getmetrics3.go:49
github.com/clbanning/mxj/examples/gonuts1.go:39: main redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/getmetrics4.go:50
github.com/clbanning/mxj/examples/gonuts1a.go:26: msg1 redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/gonuts1.go:26
github.com/clbanning/mxj/examples/gonuts1a.go:37: msg2 redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/gonuts1.go:37
github.com/clbanning/mxj/examples/gonuts1a.go:39: main redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/gonuts1.go:39
github.com/clbanning/mxj/examples/gonuts2.go:62: main redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/gonuts1a.go:39
github.com/clbanning/mxj/examples/gonuts3.go:24: xmldata redeclared in this block
previous declaration at github.com/clbanning/mxj/examples/books.go:34
github.com/clbanning/mxj/examples/gonuts3.go:24: too many errors
I have the following XML:
<rss xmlns:g="http://base.google.com/ns/1.0" version="2.0">
<channel>
<item>
<g:id>30102</g:id>
<g:title>Mini Drone Inteligente - Branco</title>
<g:price unit="BRL">149.90</g:price>
</item>
</channel>
</rss>
After parsing XML I got the result:
map[
title:Mini Drone Inteligente - Branco
id:30102
price:map[-unit:BRL #text:219.90]
]
Notice that the fields had differently results of each other, price
had result differently from others.
How I can get the same result for all fields? Like below:
map[
title:map[#text: Mini Drone Inteligente - Branco]
id:map[#text: 30102 ]
price:map[-unit:BRL #text:219.90]
]
Please add json.Number in func mapToXmlIndent.
Bcz mxj.AnyXmlIdent() not work properly. Example:
func main() {
var data = `{"country_code": "US", "language_code": "en", "gatekeepers": {"sulgin": true}, "show_app_install": true, "static_root": "//instagramstatic-a.akamaihd.net/h1", "platform": "web", "hostname": "www.instagram.com", "entry_data": {"PostPage": [{"media": {"caption_is_edited": false, "code": "BDjodwSyGGq", "date": 1459297046, "dimensions": {"width": 480, "height": 480}, "usertags": {"nodes": []}, "comments_disabled": false, "comments": {"count": 0, "page_info": {"has_previous_page": false, "start_cursor": null, "end_cursor": null, "has_next_page": false}, "nodes": []}, "id": "1216994290900165034", "caption": "#\u043d\u0430\u0448\u0430\u0440\u0430\u0448\u0430 #\u043d\u0430\u0448\u0438\u043b\u044e\u0434\u0438 #\u0432\u0441\u0448\u0430 #\u043d\u0430\u0448\u0438\u0432\u0441\u0448\u0430 #\u0440\u0443\u0441\u0441\u043a\u0438\u0435\u0430\u043c\u0435\u0440\u0438\u043a\u0430\u043d\u0446\u044b #\u044d\u043c\u0438\u0433\u0440\u0430\u043d\u0442\u044b #\u0431\u0438\u0437\u043d\u0435\u0441\u0434\u043b\u044f\u0432\u0441\u0435\u0445 #\u0440\u0435\u043a\u043b\u0430\u043c\u0430 #\u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043b\u044c\u0442\u043e\u0440\u0433\u043e\u0432\u043b\u0438 #\u044d\u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f #\u044d\u043c\u0438\u0433\u0440\u0430\u043d\u0442 \ud83d\udcf0\ud83d\udcf0\ud83d\udcf0", "likes": {"count": 43, "viewer_has_liked": false, "nodes": [{"user": {"username": "pldobedova.28a38", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/12930855_465421790323473_1556000816_a.jpg", "id": "3089391767"}}, {"user": {"username": "lowell.jamie", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/11856626_1037074359637483_999068832_a.jpg", "id": "2080739691"}}, {"user": {"username": "landing.page_phoenix", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/12445898_1531059877197403_1819456767_a.jpg", "id": "3077787572"}}, {"user": {"username": "sayvareli_dze", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/11032812_1584136531829418_1785931404_a.jpg", "id": "513392404"}}, {"user": {"username": "ay_santoscoy", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/14272205_1275392582495342_73128013_a.jpg", "id": "1649974424"}}, {"user": {"username": "prodvijenie.insta", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/11906088_548272111992346_1417441135_a.jpg", "id": "2215391262"}}, {"user": {"username": "artmedia_anapa", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/10358206_1553844208262789_373239023_a.jpg", "id": "1680807698"}}, {"user": {"username": "mariespb", "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/11325982_1451202301858508_2127601513_a.jpg", "id": "30643652"}}]}, "owner": {"username": "detroitexpress", "is_unpublished": false, "requested_by_viewer": false, "followed_by_viewer": false, "blocked_by_viewer": false, "profile_pic_url": "https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/12145014_544683419043251_2082408059_a.jpg", "full_name": "Detroit Express", "has_blocked_viewer": false, "id": "3053714631", "is_private": false}, "is_video": false, "is_ad": false, "display_src": "https://scontent-lga3-1.cdninstagram.com/t51.2885-15/s480x480/e35/12677559_212976009064620_1228847978_n.jpg?ig_cache_key=MTIxNjk5NDI5MDkwMDE2NTAzNA%3D%3D.2", "location": null}}]}, "qe": {"profile": {"p": {}, "g": ""}, "su_universe": {"p": {}, "g": ""}, "notif": {"p": {}, "g": ""}, "us": {"p": {"show_desktop_registration_upsell": "false"}, "g": "show_desktop_registration_upsell_05"}, "su": {"p": {"enabled": "true"}, "g": "rollout_20160325"}, "us_li": {"p": {}, "g": ""}, "discovery": {"p": {}, "g": ""}}, "display_properties_server_guess": {"viewport_width": 360, "pixel_ratio": 1.5}, "config": {"viewer": null, "csrf_token": "jKzHJyiHFEu7YqP6wFLJpc3UP4tST744"}, "environment_switcher_visible_server_guess": true}`
mxj.JsonUseNumber = true
var x interface{}
dec := json.NewDecoder(bytes.NewReader([]byte(data)))
dec.UseNumber()
if err := dec.Decode(&x); err != nil {
fmt.Printf("Unmarshal: %v", err)
}
xx, err := mxj.AnyXmlIndent(x, "", " ", "body")
if err != nil {
fmt.Println(err)
}
fmt.Println(string(xx))
}
Will print:
<body>
<config>
<csrf_token>
jKzHJyiHFEu7YqP6wFLJpc3UP4tST744
</csrf_token>
<viewer />
</config>
<country_code>
US
</country_code>
<display_properties_server_guess>
<Number>
1.5
</Number>
<Number>
360
</Number>
</display_properties_server_guess>
<entry_data>
<PostPage>
<media>
<caption>
#нашараша #нашилюди #всша #нашивсша #русскиеамериканцы #эмигранты #бизнесдлявсех #реклама #двигательторговли #эмиграция #эмигрант 📰📰📰
</caption>
<caption_is_edited>
false
</caption_is_edited>
<code>
BDjodwSyGGq
</code>
<comments>
<Number>
0
</Number>
<page_info>
<end_cursor />
<has_next_page>
false
</has_next_page>
<has_previous_page>
false
</has_previous_page>
<start_cursor />
</page_info>
</comments>
<comments_disabled>
false
</comments_disabled>
<Number>
1459297046
</Number>
<dimensions>
<Number>
480
</Number>
<Number>
480
</Number>
</dimensions>
<display_src>
https://scontent-lga3-1.cdninstagram.com/t51.2885-15/s480x480/e35/12677559_212976009064620_1228847978_n.jpg?ig_cache_key=MTIxNjk5NDI5MDkwMDE2NTAzNA%3D%3D.2
</display_src>
<id>
1216994290900165034
</id>
<is_ad>
false
</is_ad>
<is_video>
false
</is_video>
<likes>
<Number>
43
</Number>
<nodes>
<user>
<id>
3089391767
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/12930855_465421790323473_1556000816_a.jpg
</profile_pic_url>
<username>
pldobedova.28a38
</username>
</user>
</nodes>
<nodes>
<user>
<id>
2080739691
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/11856626_1037074359637483_999068832_a.jpg
</profile_pic_url>
<username>
lowell.jamie
</username>
</user>
</nodes>
<nodes>
<user>
<id>
3077787572
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/12445898_1531059877197403_1819456767_a.jpg
</profile_pic_url>
<username>
landing.page_phoenix
</username>
</user>
</nodes>
<nodes>
<user>
<id>
513392404
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/11032812_1584136531829418_1785931404_a.jpg
</profile_pic_url>
<username>
sayvareli_dze
</username>
</user>
</nodes>
<nodes>
<user>
<id>
1649974424
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/14272205_1275392582495342_73128013_a.jpg
</profile_pic_url>
<username>
ay_santoscoy
</username>
</user>
</nodes>
<nodes>
<user>
<id>
2215391262
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/11906088_548272111992346_1417441135_a.jpg
</profile_pic_url>
<username>
prodvijenie.insta
</username>
</user>
</nodes>
<nodes>
<user>
<id>
1680807698
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/10358206_1553844208262789_373239023_a.jpg
</profile_pic_url>
<username>
artmedia_anapa
</username>
</user>
</nodes>
<nodes>
<user>
<id>
30643652
</id>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/11325982_1451202301858508_2127601513_a.jpg
</profile_pic_url>
<username>
mariespb
</username>
</user>
</nodes>
<viewer_has_liked>
false
</viewer_has_liked>
</likes>
<location />
<owner>
<blocked_by_viewer>
false
</blocked_by_viewer>
<followed_by_viewer>
false
</followed_by_viewer>
<full_name>
Detroit Express
</full_name>
<has_blocked_viewer>
false
</has_blocked_viewer>
<id>
3053714631
</id>
<is_private>
false
</is_private>
<is_unpublished>
false
</is_unpublished>
<profile_pic_url>
https://scontent-lga3-1.cdninstagram.com/t51.2885-19/s150x150/12145014_544683419043251_2082408059_a.jpg
</profile_pic_url>
<requested_by_viewer>
false
</requested_by_viewer>
<username>
detroitexpress
</username>
</owner>
<usertags>
</usertags>
</media>
</PostPage>
</entry_data>
<environment_switcher_visible_server_guess>
true
</environment_switcher_visible_server_guess>
<gatekeepers>
<sulgin>
true
</sulgin>
</gatekeepers>
<hostname>
www.instagram.com
</hostname>
<language_code>
en
</language_code>
<platform>
web
</platform>
<qe>
<discovery>
<g />
<p />
</discovery>
<notif>
<g />
<p />
</notif>
<profile>
<g />
<p />
</profile>
<su>
<g>
rollout_20160325
</g>
<p>
<enabled>
true
</enabled>
</p>
</su>
<su_universe>
<g />
<p />
</su_universe>
<us>
<g>
show_desktop_registration_upsell_05
</g>
<p>
<show_desktop_registration_upsell>
false
</show_desktop_registration_upsell>
</p>
</us>
<us_li>
<g />
<p />
</us_li>
</qe>
<show_app_install>
true
</show_app_install>
<static_root>
//instagramstatic-a.akamaihd.net/h1
</static_root>
</body>
package main
import (
"fmt"
"github.com/clbanning/mxj"
"testing"
)
type ItemInfo struct {
Id int `xml:"Id"`
Name string `xml:"Name"`
}
func TestXmlToMap(t *testing.T) {
items := make([]ItemInfo, 2)
items[0].Id = 1
items[0].Name = "item_name_1"
items[1].Id = 2
items[1].Name = "item_name_2"
data := map[string]interface{}{
"InvoDesc": "desc",
"Orders": map[string]interface{}{
"Order":map[string]interface{}{
"BillNo":"billno",
"Items":map[string]interface{}{
"Item":items,
//"Item":[]map[string]interface{} {{"Id":1, "Name":"item_name_1"}, {"Id":2, "Name":"item_name_2"}},
},
},
},
}
mxjXmlStr, err := mxj.Map(data).Xml("InvoInfo")
if err != nil {
fmt.Println(err)
return
}
fmt.Println("mxj Xml: ", string(mxjXmlStr))
}
Results after implementation is
<InvoInfo><InvoDesc>desc</InvoDesc><Orders><Order><BillNo>billno</BillNo><Items><ItemInfo><Id>1</Id><Name>item_name_1</Name></ItemInfo><ItemInfo><Id>2</Id><Name>item_name_2</Name></ItemInfo></Item></Items></Order></Orders></InvoInfo>
The expected result is
<InvoInfo><InvoDesc>desc</InvoDesc><Orders><Order><BillNo>billno</BillNo><Items><ItemInfo><Id>1</Id><Name>item_name_1</Name></ItemInfo><ItemInfo><Id>2</Id><Name>item_name_2</Name></ItemInfo></Items></Order></Orders></InvoInfo>
Please help to see where there is a mistake
Thanks for the great library, it is of great help.
I have a use case, In which I need to skip casting of value for few keys to numeric/bool.
We can provide a function which takes in a regex/list of keys and sets them to skip casting for those keys.
Please let me know, what you think, I can write the function for this, and create a pull request.
Hi there,
I would like to know if there is way to parse partial data located in different places in the xml doc. For example, I want to got from
`<a>
<b>
<c>c</c>
<d>d</d>
</b>
<e>
<x>x</x>
<y>y</y>
<z>z</z>
</e>
<f>f</f>
<g>g</g>
</a>`
to
`<a>
<b>
<c>c</c>
</b>
<e>
<x>x</x>
</e>
</a>`
Is there any easy way to do that plz?
Thanks in advance!
When getting the following perfectly valid XML (a response from PANW Wildfire component):
`
2.0
<file_info>
yes
b918d3b98f590d99ac3694b373958017256a22b4
PE
dd8e64991f68eeebb52c9947ea56f39a776185d1a076741533aa6843022c03b0
7f638f13d0797ef9b1a393808dc93b94
55296
</file_info>
<task_info>
3.0
2
Windows XP, Adobe Reader 9.4.0, Flash 10, Office 2007
dd8e64991f68eeebb52c9947ea56f39a776185d1a076741533aa6843022c03b0
7f638f13d0797ef9b1a393808dc93b94
55296
yes
When converting from XML to a map I have found that a string value of "NAN", gets cast to a float value as a NaN. This is not the desired result in this particular case, as it is actually a string value.
There is no NaN equivalent in JSON, so when converting from a Map to JSON, I get an error:
json: unsupported value: NaN
I assume this issue would also arise if there was a string value of "Inf" or "-Inf".
I have written a small example below to replicate the issue.
package main
import (
"fmt"
"github.com/clbanning/mxj"
)
func main() {
var xml = []byte("<foo><bar>NAN</bar></foo>")
m, err := mxj.NewMapXml(xml, true)
if err != nil {
panic(err)
}
fmt.Print(m.StringIndent())
fmt.Println()
j, err := m.JsonIndent("", " ", true)
if err != nil {
panic(err)
}
fmt.Print(string(j))
}
Hi,
It seems like special xml characters don't get encoded:
Test case:
json := "{ \"test\": \"a&b <c>\" }"
xml, _ := j2x.JsonToXml([]byte(json))
fmt.Printf("xml %v\n", string(xml))
Output:
xml <test>a&b <c></test>
How can we encode these values?
It seems to be a good idea to replace in tests fmt.Println to testing.T.Log. Because it's hard to look for my fail message during tests. What do you think?
I don't know if this is the intended behavior, but mxj.NewMapXml() seem to unmarshal all XML elements to a string. In my case I'd have expected at least integers and bool values to be unmarshalled without quotes.
Here's an example to help illustrate the problem: http://play.golang.org/p/ZhGqn4EWHD
Otherwise I like this package, and perhaps I'll do a fork to implement this functionality, if it's not already there (undocumented) - it should be fairly easy to decide if the value is a string, int or bool, and for other types we would even add an attribute on the element to check against.
Again I don't know if this functionality is left out intentionally since perhaps you do not consider this a responsibility for this package, but something we should handle in our own code?
Hi Charles,
Any plan to add JSONPath support for mxj?
Ref: https://github.com/jarcoal/jpath & http://goessner.net/articles/JsonPath/.
Thx.
Thanks for the awesome library. It scratches the itch that I have for dynamically parsing XML perfectly.
However, if you try to parse an rdf/xml document with this library, you get an index out of bounds panic.
Hi,
In your documentation, you state that the code is licensed under the BSD 3 clause license.
When I reviewed the license file, it states first your name and the MIT License and then Go Language followed by the BSD 3 clause license.
Both are perfectly cool licenses, but could you clear up which one is the one that you actually selected?
Thanks
m, err := mxj.NewMapJson([]byte(ji))
if err != nil {
log.Println("err: ", err.Error())
}
fmt.Println(m1)
x, _ := m.XmlSeqIndent("", " ")
fmt.Println(string(x))
Output XML has been changed the #seq order.
I just changed the XmlSeq parser to preserve name space prefixes as part of the key as well as preserving "xmlns:" in the attribute label. This lets you re-encode a Map value correctly when the original XML used name space qualified tags.
Is there a case to make this functionality part of the standard Xml parser - NewMapXml(), etc.?
Thanks for the example in #24. The only issue is that the outputted JSON has an extra "EOF" at the end. How can this be removed from the output?
map.Xml() time grows exponentially for large map.
For example if we read 1MB XML and serialize it back it take almost 20s and if we double the size (duplicate the xml nodes from same input) then time taken is almost 90s.
I tested with this xml (http://aiweb.cs.washington.edu/research/projects/xmltk/xmldata/data/mondial/mondial-3.0.xml) with following code.
package main
import (
"fmt"
"io/ioutil"
"time"
mxj "github.com/clbanning/mxj"
)
func main() {
b, _ := ioutil.ReadFile("/tmp/large.xml")
start := time.Now()
m, _ := mxj.NewMapXml(b)
fmt.Printf("XML Read time %v\n", time.Now().Sub(start))
start = time.Now()
b, _ = m.Xml()
fmt.Printf("XML Write time %v\n", time.Now().Sub(start))
}
I debugged further and I think it is because of string concatenation (*s += ). I created a simple code to string concatenation and found that it is very slow as string grows. When I replaced it by bytes.Buffer.Read then there was significant improvement.
could anyone provide a compilable example using x2j for this simple task?
When using the http.Response.Body as a reader to pass to NewMapXMLReader
, it fails to parse XML responses not ending with a line break.
Here a test showing the error: https://play.golang.org/p/ob6DnGUwtLx
I was able to fix it in my project using this wrapper over the http.Response.Body
:
type xmlReader struct {
r io.Reader
}
func (x xmlReader) Read(p []byte) (n int, err error) {
n, err = x.r.Read(p)
if err != io.EOF {
return n, err
}
if len(p) == n {
return n, nil
}
p[n] = ([]byte("\n"))[0]
return n + 1, err
}
It's not pretty but it worked https://play.golang.org/p/bITvPyFHuZ6
Don't you think this should be managed by the mxj lib itself?
I found another issue. Attached is the testcase. So, I read in the kml file and wanted to find all the description tags and change their values. And, some of the values did not change. If you run the code as is you will see the errors:
ERROR Value not updated. Path=kml.Document.Folder.Placemark.description, oldVal=description!]?:@#Click on the blue link!
Placemark descriptions can be enriched by using many standard HTML tags.
, newVal=description!]?:@#Click on the blue link!<br><br>
Placemark descriptions can be enriched by using many standard HTML tags.<br> CHANGED
ERROR Value not updated. Path=kml.Document.Folder.Document.description, oldVal=description!]?:@#Place your mouse over the icon to see it display the new
icon, newVal=description!]?:@#Place your mouse over the icon to see it display the new
icon CHANGED
ERROR Value not updated. Path=kml.Document.description, oldVal=description!]?:@#Unleash your creativity with the help of these examples!, newVal=description!]?:@#Unleash your creativity with the help of these examples! CHANGED
The function that I call to change all the values is :
kmlMap.UpdateValuesForPath(newVal, path, oldVal)
If I go to function updateValue() which is called by UpdateValuesForPath(), and change the line 199 from:
if len(subkeys) == 0 {
====== TO =========
if hasSubKeys(m, subkeys) {
Then the errors go away and all the descriptions are changed. Now, there is also another place in that function that calls "len(subkeys)==0" on line 177. My sample data doesn't take me into that branch of code. But, I assume that if line 199 needs to be changed, then line 178 does too.
This line ("len(subkeys)==0") is also called three times in keyvalues.go in other functions. Does it need to be changed there as well?
Release 1.6.1 does not pass unit tests with Go 1.10. At least:
+ GOPATH=/builddir/build/BUILD/mxj-1.6.1/_build:/usr/share/gocode
+ go test -buildmode pie -compiler gc -ldflags '-extldflags '\''-Wl,-z,relro '\'''
# github.com/clbanning/mxj
./anyxml_test.go:10: Println arg list ends with redundant newline
./badxml_test.go:24: Println arg list ends with redundant newline
./bom_test.go:22: Println arg list ends with redundant newline
./bulk_test.go:12: Println arg list ends with redundant newline
./bulkraw_test.go:12: Println arg list ends with redundant newline
./files_test.go:9: Println arg list ends with redundant newline
./j2x_test.go:11: Println arg list ends with redundant newline
./json_test.go:15: Println arg list ends with redundant newline
./keyvalues_test.go:13: Println arg list ends with redundant newline
./keyvalues_test.go:227: Println arg list ends with redundant newline
./mxj_test.go:9: Println arg list ends with redundant newline
./namespace_test.go:9: Println arg list ends with redundant newline
./nan_test.go:11: Println arg list ends with redundant newline
./newmap_test.go:11: Println arg list ends with redundant newline
./seqnum_test.go:29: Println arg list ends with redundant newline
./struct_test.go:9: Println arg list ends with redundant newline
./updatevalues_test.go:11: Println arg list ends with redundant newline
./xml2_test.go:13: Println arg list ends with redundant newline
./xml_test.go:11: Println arg list ends with redundant newline
./xmlseq_test.go:10: Println arg list ends with redundant newline
FAIL github.com/clbanning/mxj [build failed]
This may panic at runtime: https://github.com/clbanning/mxj/blob/master/set.go#L19
I can't figure out how to append to a list.
I want to change
<a>
<b>1</b>
</a>
to
<a>
<b>1</b>
<b>2</b>
</a>
hi,
I'm dealing with some XML files that have <?xml version="1.0" encoding="utf-8"?>
at top of the file by using the NewMapXmlSeq
cause I want to preserve the format and some comments in the file.
But I could only get the result map[#procinst:map[#target:xml #inst:version="1.0" encoding="utf-8"]]
,
and the returned error is no root key
As you mentioned in the doc:
comments, directives, and procinsts that are NOT part of a document with a root key will be returned as
map[string]interface{} and the error value 'NoRoot'.
How can I get the whole map of the XML file?
I thought it's quite common that <?xml version="1.0" encoding="utf-8"?>
not belong to any root
Hi I'm trying to convert JSON to XML without A-Z sorting, Is that possible?
Thank you
mxj isn't building currently. Here's what I get...
$ go get github.com/clbanning/mxj
# github.com/clbanning/mxj
src/github.com/clbanning/mxj/xml.go:471:40: cannot convert t (type xml.Token) to type string: need type assertion
markbr@A31217MarkBr:~/test$
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.