Code Monkey home page Code Monkey logo

zio-json's Introduction

ZIO JSON

ZIO Json is a fast and secure JSON library with tight ZIO integration.

Production Ready CI Badge Sonatype Releases Sonatype Snapshots javadoc ZIO JSON

Introduction

The goal of this project is to create the best all-round JSON library for Scala:

  • Performance to handle more requests per second than the incumbents, i.e. reduced operational costs.
  • Security to mitigate against adversarial JSON payloads that threaten the capacity of the server.
  • Fast Compilation no shapeless, no type astronautics.
  • Future-Proof, prepared for Scala 3 and next-generation Java.
  • Simple small codebase, concise documentation that covers everything.
  • Helpful errors are readable by humans and machines.
  • ZIO Integration so nothing more is required.

Installation

In order to use this library, we need to add the following line in our build.sbt file:

libraryDependencies += "dev.zio" %% "zio-json" % "0.6.2"

Example

Let's try a simple example of encoding and decoding JSON using ZIO JSON.

All the following code snippets assume that the following imports have been declared

import zio.json._

Say we want to be able to read some JSON like

{"curvature":0.5}

into a Scala case class

case class Banana(curvature: Double)

To do this, we create an instance of the JsonDecoder typeclass for Banana using the zio-json code generator. It is best practice to put it on the companion of Banana, like so

object Banana {
  implicit val decoder: JsonDecoder[Banana] = DeriveJsonDecoder.gen[Banana]
}

Note: If you’re using Scala 3 and your case class is defining default parameters, -Yretain-trees needs to be added to scalacOptions.

Now we can parse JSON into our object

scala> """{"curvature":0.5}""".fromJson[Banana]
val res: Either[String, Banana] = Right(Banana(0.5))

Likewise, to produce JSON from our data we define a JsonEncoder

object Banana {
  ...
  implicit val encoder: JsonEncoder[Banana] = DeriveJsonEncoder.gen[Banana]
}

scala> Banana(0.5).toJson
val res: String = {"curvature":0.5}

scala> Banana(0.5).toJsonPretty
val res: String =
{
  "curvature" : 0.5
}

And bad JSON will produce an error in jq syntax with an additional piece of contextual information (in parentheses)

scala> """{"curvature": womp}""".fromJson[Banana]
val res: Either[String, Banana] = Left(.curvature(expected a number, got w))

Say we extend our data model to include more data types

sealed trait Fruit
case class Banana(curvature: Double) extends Fruit
case class Apple (poison: Boolean)   extends Fruit

we can generate the encoder and decoder for the entire sealed family

object Fruit {
  implicit val decoder: JsonDecoder[Fruit] = DeriveJsonDecoder.gen[Fruit]
  implicit val encoder: JsonEncoder[Fruit] = DeriveJsonEncoder.gen[Fruit]
}

allowing us to load the fruit based on a single field type tag in the JSON

scala> """{"Banana":{"curvature":0.5}}""".fromJson[Fruit]
val res: Either[String, Fruit] = Right(Banana(0.5))

scala> """{"Apple":{"poison":false}}""".fromJson[Fruit]
val res: Either[String, Fruit] = Right(Apple(false))

Almost all of the standard library data types are supported as fields on the case class, and it is easy to add support if one is missing.

import zio.json._

sealed trait Fruit                   extends Product with Serializable
case class Banana(curvature: Double) extends Fruit
case class Apple(poison: Boolean)    extends Fruit

object Fruit {
  implicit val decoder: JsonDecoder[Fruit] =
    DeriveJsonDecoder.gen[Fruit]

  implicit val encoder: JsonEncoder[Fruit] =
    DeriveJsonEncoder.gen[Fruit]
}

val json1         = """{ "Banana":{ "curvature":0.5 }}"""
val json2         = """{ "Apple": { "poison": false }}"""
val malformedJson = """{ "Banana":{ "curvature": true }}"""

json1.fromJson[Fruit]
json2.fromJson[Fruit]
malformedJson.fromJson[Fruit]

List(Apple(false), Banana(0.4)).toJsonPretty

How

Extreme performance is achieved by decoding JSON directly from the input source into business objects (inspired by plokhotnyuk). Although not a requirement, the latest advances in Java Loom can be used to support arbitrarily large payloads with near-zero overhead.

Best in class security is achieved with an aggressive early exit strategy that avoids costly stack traces, even when parsing malformed numbers. Malicious (and badly formed) payloads are rejected before finishing reading.

Fast compilation and future-proofing is possible thanks to Magnolia which allows us to generate boilerplate in a way that will survive the exodus to Scala 3. zio-json is internally implemented using a java.io.Reader / java.io.Writer-like interface, which is making a comeback to center stage in Loom.

Simplicity is achieved by using well-known software patterns and avoiding bloat. The only requirement to use this library is to know about Scala's encoding of typeclasses, described in Functional Programming for Mortals.

Helpful errors are produced in the form of a jq query, with a note about what went wrong, pointing to the exact part of the payload that failed to parse.

Documentation

Learn more on the ZIO JSON homepage!

Contributing

For the general guidelines, see ZIO contributor's guide.

Code of Conduct

See the Code of Conduct

Support

Come chat with us on Badge-Discord.

Acknowledgement

License

License

zio-json's People

Contributors

987nabil avatar adamgfraser avatar brbrown25 avatar calvinlfer avatar earldouglas avatar erikvanoosten avatar fanf avatar fommil avatar fsvehla avatar ghostdogpr avatar github-actions[bot] avatar guersam avatar guizmaii avatar jdegoes avatar khajavi avatar kitlangton avatar laogao avatar mijicd avatar plokhotnyuk avatar remiguittaut avatar renovate[bot] avatar scala-steward avatar senia-psm avatar sergeda avatar sh0hei avatar sideeffffect avatar softinio avatar steinybot avatar thijsbroersen avatar vigoo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

zio-json's Issues

Proposal to implement from `String` instead that from `Char`

I would like to implement a version that actually takes either a String. This is because in zio-s3 files get streamed with Bytes which can be conveniently translated to String but not to Char.

Am I missing some transducer that would help my case?

I also could not find a utility method that creates a Transducer[E, I, Chunk[O]] from a I => Chunk[O] which would have solved my use case quickly. Would that be a better and more useful piece of code to write?

Add microsite for ZIO JSON

This ticket will be easier to complete after #9, at which point a Docusaurus-based microsite could be created that has basic information on getting started (i.e. some of the information in the README, but expanded).

ZIO JSON 1.0

  • Complete microsite
  • Break out integrations into separate modules
  • Creating integrations with mainstream web servers
  • Adding methods to the JSON AST — #53
  • Codec-time cursors (not parsing the stuff you're not interested in)
  • Comprehensive codecs for Scala & Java types
  • Golden testing
  • Testkit (ZIO Test assertions, diffs, patch, etc.)

Support enum encoding

It would be nice to have support for encoding enums as string values, typically sealed traits of only case objects.

For example:

sealed trait StreamStatus
case object UPDATING extends StreamStatus
case object CREATING extends StreamStatus

would be encoded as "UPDATING" or "CREATING"

Cannot round trip encode/decode duration with nanoseconds

Encoding a large duration (Duration.ofSeconds(Long.MaxValue, 999_999_999L)) with nanoseconds can fail with an ArithmeticException:

        java.lang.ArithmeticException: long overflow
        	at java.base/java.lang.Math.multiplyExact(Math.java:949)
        	at java.base/java.lang.Math.multiplyExact(Math.java:925)
        	at java.base/java.time.Duration.toMillis(Duration.java:1226)
        	at zio.json.EncoderLowPriority3.$anonfun$duration$1(encoder.scala:318)
        	at zio.json.JsonEncoder$$anon$5.unsafeEncode(encoder.scala:172)

Rather than using milliseconds (which also looses precision) we should using ISO-8601 duration format.

`orElse` decoder combinator throws IndexOutOfBound exception

A soos as someone try to use JsonDecoder.orElse, we get that stack trace:

java.lang.ArrayIndexOutOfBoundsException: 0
	at zio.json.internal.WithRecordingReader.readChar(readers.scala:185)
	at zio.json.internal.OneCharReader.nextNonWhitespace(readers.scala:32)
	at zio.json.internal.OneCharReader.nextNonWhitespace$(readers.scala:30)
	at zio.json.internal.WithRecordingReader.nextNonWhitespace(readers.scala:158)
	at zio.json.internal.Lexer$.char(lexer.scala:283)
	at zio.json.internal.Lexer$.string(lexer.scala:140)
	at zio.json.JsonDecoder$$anon$4.unsafeDecode(decoder.scala:303)
	at zio.json.JsonDecoder$$anon$4.unsafeDecode(decoder.scala:301)
	at zio.json.JsonDecoder$$anon$2.unsafeDecode(decoder.scala:217)
	at zio.json.JsonDecoder$$anon$1.unsafeDecode(decoder.scala:191)
	at zio.json.JsonDecoder.decodeJson(decoder.scala:51)
	at zio.json.JsonDecoder.decodeJson$(decoder.scala:50)
	at zio.json.JsonDecoder$$anon$1.decodeJson(decoder.scala:187)
	at zio.json.package$DecoderOps$.fromJson$extension(package.scala:23)

The cause is that orElse use WithRecordingReader with an initial value of 0.

Eliminate use of Reader / Writer

Most likely we can improve performance by eliminating read / writer and going directly with inputstream / outputstream or classes baked on these.

JsonCodec ≠ JsonDecoder + JsonEncoder: could not find JsonCodec.Typeclass for type String

Hi everyone,

I hope it's not too early to report things.

I tried to replace Circe in a Scala/ScalaJS project and I had the following errors:

[error] /Users/jules.ivanic/jules_ivanic/workspace/http4s-laminar-stack/modules/shared/src/main/scala/example/shared/Protocol.scala:10:70: magnolia: could not find JsonCodec.Typeclass for type String
[error]     in parameter 'search' of product type example.shared.Protocol.GetSuggestions.Request
[error]       implicit final val codec: JsonCodec[Request] = DeriveJsonCodec.gen
[error]                                                                      ^
[error] /Users/jules.ivanic/jules_ivanic/workspace/http4s-laminar-stack/modules/shared/src/main/scala/example/shared/Protocol.scala:15:71: magnolia: could not find JsonCodec.Typeclass for type Seq[String]
[error]     in parameter 'suggestions' of product type example.shared.Protocol.GetSuggestions.Response
[error]       implicit final val codec: JsonCodec[Response] = DeriveJsonCodec.gen
[error]                                                                       ^
[error] two errors found

It's open-source so you can easily reproduce it. See: https://github.com/keynmol/http4s-laminar-stack/pull/4/files

Add transformation and composition operators to JsonCodec

Just as JsonEncoder and JsonDecoder support a wide range of operators, which transform and combine codecs, so also can JsonCodec support operators, including:

  • orElse
  • orElseEither
  • zip
  • zipLeft
  • zipRight

These will have type signatures similar to the existing type signatures.

In addition, an xmapOrFail could be added, probably here and to the parent types.

Silence or fix warnings

#21 introduced more exhaustive warnings.

Fix what is possible, silence what are false positives, re-enable fatal warnings.

Add methods to JSON AST

  • final def transformAt[A <: Json](cursor: JsonCursor[_, A])(f: A => Json): Either[String, Json] = ???
  • final def delete(cursor: JsonCursor[_, _]): Either[String, Json] = ???
  • final def diff(that: Json): JsonDiff = JsonDiff(self, that)
  • final def intersect(that: Json): Either[String, Json] = ???
  • final def relocate(from: JsonCursor[_, _], to: JsonCursor[_, _]): Either[String, Json] = ???

seeing error "magnolia: could not find any direct subtypes of trait" when its not true!

The pattern seems to be when the base sealed trait has several parameters
eg
sealed trait Foo[A]

but the child class instantiates them

case class Bar(..) extends Foo[SomeConcreteType]

i'm hoping its other stuff that spuriously confusing zio-json, because overall i'm finding it to be a very humane tool! (though i did have to roll my own Map[k,v] instance to get some stuff to work )

Pull out integrations into separate modules

For independent versioning, I think pulling out the integrations into separate modules may make sense. Otherwise they must all proceed in lock-step with every update to each library or to Scala.

Parital Data / Missing fields

Hi All,
Is there any way to handle missing fields in the data with defaults like

case class Test(value: String = "N/A", weight: Int = 0)

"""{ "weight": 1}""".fromJson[Test]

Quotes missing from java.time encoders

@ JsonDecoder[DayOfWeek].decodeJson(JsonEncoder[DayOfWeek].encodeJson(DayOfWeek.MONDAY, None))
res17: Either[String, DayOfWeek] = Left("(expected '\"' got 'M')")

Manual decoder for sum type without type hint

Often, especially when the JSON is a given spec (for ex from an API), we don't have the choice to have a type int for the encoding of a sum type, which is nonetheless possible to parse just based on attributes.

For example, consider the case where you have an array of fruits, Apple and Banana, like in zio-json doc:

[{"poison":false}, {"curvature":0.5}]

And you would like to parse that in the your business domain into the following sum type:

sealed trait Fruit
final case class Banana(curvature: Double) extends Fruit
final case class Apple (poison: Boolean)   extends Fruit

This is possible and deterministic but for that, we need to manually encode the fruit decoder. We could use tuple decoder and orElse , but it quickly become fastidious and inefficient.

We would like to be able to direct the chained decoder directly by pattern-matching on the first characters of the attribute name.

And other, similar use case, is when we need to pattern-match based on the value directly, like in:

[ false, 0.5 ] => List(Apple(false), Banana(0.5))

@fommil provided the basic layout of such a decoder generator in https://discord.com/channels/629491597070827530/733728086637412422/785790333027614730:


import scala.annotation.switch

import zio.json._
import zio.json.internal.RetractReader
import Decoder.{JsonError, UnsafeJson}

sealed trait Simple
object Simple {
  case class S(a: String)  extends Simple
  object S {
    implicit val decoder: Decoder[S] = Decoder[String].map(S(_))
  }

  case class B(a: Boolean) extends Simple
  object B {
    implicit val decoder: Decoder[B] = Decoder[Boolean].map(B(_))
  }

  implicit val boolean: Decoder[Simple] = new Decoder[Simple] {
    def unsafeDecode(trace: List[JsonError], in: RetractReader): Simple =
      (in.nextNonWhitespace(): @switch) match {
        case '"' =>
          in.retract()
          Decoder[S].unsafeDecode(JsonError.SumType("S") :: trace, in)
        case 't' | 'f' =>
          in.retract()
          Decoder[B].unsafeDecode(JsonError.SumType("B") :: trace, in)
        case c =>
          throw UnsafeJson(JsonError.Message(s"expected string or boolean got $c") :: trace)
    }
  }
}

manualSumSwitch(in) {
  case '"' => ("S", Decoder[S])
  case 't' | 'f' => ("B", Decoder[B])
}

or maybe the SumType would be dependent on the character encountered, so it could be

manualSumSwitch(in) {
  case '"' => Decoder[S]
  case 't' | 'f' => Decoder[B]
}

(plus some other comment latter on the discussion).

Generated encoder for a case class with None produces invalid json

case class User (name: Option[String], age: Int)
object User { 
  implicit val encoder = DeriveJsonEncoder.gen[User]
}
scala> User(None, 37).toJson
res7: String = {,"age":37}

Note the extra comma at the beginning.

If the fields are re-ordered, the json is fine:

case class User (age: Int, name: Option[String])
object User { 
  implicit val encoder = DeriveJsonEncoder.gen[User]
}
scala> User(36, None).toJson
res9: String = {"age":36}

JSONTestSuite failures

Success([-2]) did not satisfy (fails(anything()) ?? "n_number_-2..json")
Success([12]) did not satisfy (fails(anything()) ?? "n_number_with_leading_zero.json")
Success("") did not satisfy (fails(anything()) ?? "n_string_with_trailing_garbage.json")
Success([2E+3]) did not satisfy (fails(anything()) ?? "n_number_2.e3.json")
Success({"a":true}) did not satisfy (fails(anything()) ?? "n_structure_object_with_trailing_garbage.json")
Success([-1]) did not satisfy (fails(anything()) ?? "n_number_-01.json")
Success({"a":"b"}) did not satisfy (fails(anything()) ?? "n_object_trailing_comment_slash_open.json")
Success(1) did not satisfy (fails(anything()) ?? "n_structure_close_unopened_array.json")
Success({"a":"b"}) did not satisfy (fails(anything()) ?? "n_object_trailing_comment_open.json")
Success({"a":"b"}) did not satisfy (fails(anything()) ?? "n_object_trailing_comment_slash_open_incomplete.json")
Success([2E+3]) did not satisfy (fails(anything()) ?? "n_number_2.e+3.json")
Success([1]) did not satisfy (fails(anything()) ?? "n_structure_array_trailing_garbage.json")
Success([]) did not satisfy (fails(anything()) ?? "n_structure_double_array.json")
Success([-12]) did not satisfy (fails(anything()) ?? "n_number_neg_int_starting_with_zero.json")
Failure(Die(java.lang.StackOverflowError)) did not satisfy (fails(anything()) ?? "n_structure_100000_opening_arrays.json")
Success({}) did not satisfy (fails(anything()) ?? "n_structure_object_followed_by_closing_object.json")
Success(123) did not satisfy (fails(anything()) ?? "n_multidigit_number_then_00.json")
Success([0.002]) did not satisfy (fails(anything()) ?? "n_number_2.e-3.json")
Failure(Die(java.lang.StackOverflowError)) did not satisfy (fails(anything()) ?? "n_structure_open_array_object.json")
Success({"a":"b"}) did not satisfy (fails(anything()) ?? "n_object_with_trailing_garbage.json")
Success({"a":"b"}) did not satisfy (fails(anything()) ?? "n_structure_trailing_#.json")
Success([1]) did not satisfy (fails(anything()) ?? "n_structure_array_with_extra_array_close.json")
Success([-0.123]) did not satisfy (fails(anything()) ?? "n_number_neg_real_without_int_part.json")
Success({"a":"b"}) did not satisfy (fails(anything()) ?? "n_object_trailing_comment.json")
Success(2) did not satisfy (fails(anything()) ?? "n_structure_number_with_trailing_garbage.json")
Success([1]) did not satisfy (fails(anything()) ?? "n_number_real_without_fractional_part.json")
Success([""]) did not satisfy (fails(anything()) ?? "n_array_comma_after_close.json")
Success([0E+1]) did not satisfy (fails(anything()) ?? "n_number_0.e1.json")
Success(["x"]) did not satisfy (fails(anything()) ?? "n_array_extra_close.json")

We parse all documents that we should, but don’t fail some that should fail - seems like the most are related to invalid input after we’ve ended parsing, but there are also some Stack overflows in there.

Files from here: https://github.com/nst/JSONTestSuite/tree/master/test_parsing

PR with tests incoming.

Combine encoding as an array of values and JsonDiscriminator

Hello,

JMAP is encoding some data, called Invocation in the following way:

["nameOfTheMethod", arguments, "clientId"]

It's an array of 3 elements, the first and the third are string, while the second is an object which type depends on the first.

So for example

"""["Mailbox/get", {}, "0"]""".fromJson[Invocation]

should return:

Either[String, MailboxGet] = Right(MailboxGet("Mailbox/get", MailboxGetArguments(...), "0"))

while

"""["Mailbox/query", {}, "0"]""".fromJson[Invocation]

should return:

Either[String, MailboxQuery] = Right(MailboxQuery("Mailbox/query", MailboxQueryArguments(...), "0"))

given:

sealed trait Invocation
case class MailboxGet(methodName: String, args: MailboxGetArguments, clientId: String) extends Invocation
case class MailboxQuery(methodName: String, args: MailboxQueryArguments, clientId: String) extends Invocation

Is there a way to handle this case with zio-json?

Rename annotations

A goal of this library is to ensure that a single import can bring everything into scope in a way that is unlikely to suffer from name clashes with other entities in the user's application.

In other words, this should be all that is necessary to use ZIO JSON:

import zio.json._

We are nearly to this goal, however, there are several annotations that need to be renamed:

  1. field => jsonField
  2. discriminator => jsonDiscriminator
  3. hint => jsonHint
  4. no_extra_fields => jsonNoExtraFields

These annotations are located in the file macros.scala inside /src/main/scala/zio/json. In addition, usages of these annotations exist inside the test directory (and maybe benchmarks), and there is documentation for these annotations in README.md that needs to be updated.

Cannot round trip encode/decode large years

Once #115 is fixed then small and large years cannot be encoded and then decoded.

For example:

Year.of(10000).toJson.fromJson

Fails with:

Left((10000 is not a valid ISO-8601 format, Text '10000' could not be parsed at index 0))

ZIO JSON 0.1

  • Fix Scala.js build (#50)
  • All JsonTestSuite tests should pass

Add helper methods in zio.json package object to read / write from URLs / files

sealed trait JsonStreamDelimiter
object JsonStreamDelimiter{
  case object Newline extends JsonStreamDelimiter
  case object Array extends JsonStreamDelimiter
}
def readJson(url: URL, jsonDelimiter: JsonStreamDelimiter = JsonStreamDeliminter.Newline): ZStream[Blocking, IOException, Json]
def readJson(file: String, jsonDelimiter: JsonStreamDelimiter = JsonStreamDeliminter.Newline): ZStream[Blocking, IOException, Json]
def readJson(file: Path, jsonDelimiter: JsonStreamDelimiter = JsonStreamDeliminter.Newline): ZStream[Blocking, IOException, Json]
def readJson(file: java.io.File, jsonDelimiter: JsonStreamDelimiter = JsonStreamDeliminter.Newline): ZStream[Blocking, IOException, Json]
def writeJson(file: String, stream: ZStream[Blocking, IOException, Json], jsonDelimiter: JsonStreamDelimiter = JsonStreamDeliminter.Newline): ZIO[Blocking, IOException, Unit]
def writeJson(file: Path, stream: ZStream[Blocking, IOException, Json], jsonDelimiter: JsonStreamDelimiter = JsonStreamDeliminter.Newline): ZIO[Blocking, IOException, Unit]
def writeJson(file: java.io.File, stream: ZStream[Blocking, IOException, Json], jsonDelimiter: JsonStreamDelimiter = JsonStreamDeliminter.Newline): ZIO[Blocking, IOException, Unit]

case class default field values

It would be useful to support default field values when decoding.

case class Entity(id: Long, description: String = "")

"""{"id":42}""".fromJson[Entity] == Right(Entity(42L, ""))

I can provide a PR if needed.

Revert error trace to use List instead of Chunk

As per @fommil, error trace is constructed in the happy path and therefore needs to be as fast as possible. Currently this code uses Chunk which is more geared at smaller memory footprint and faster read access, but this use case demands fastest possible write performance.

All error traces should be reverted to use List instead of Chunk.

Scala 3 support

We should support Scala 3 with the same API as Scala 2. This way users can easily transition from Scala 2 to Scala 3 without having to change any of their ZIO JSON code.

There are several steps to this support:

  1. Enable cross-compilation, which will be easier after #9
  2. Enable type class derivation, this could be done via patching Magnolia or independently via Scala 3 type class derivation

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.