Skip to content

Commit 1dd3c62

Browse files
committed
Site updated at 2014-11-16 05:11:35 UTC
1 parent d253773 commit 1dd3c62

File tree

16 files changed

+2949
-7
lines changed

16 files changed

+2949
-7
lines changed

atom.xml

Lines changed: 293 additions & 1 deletion
Large diffs are not rendered by default.

blog/2014/03/09/json-and-scala/index.html

Lines changed: 524 additions & 0 deletions
Large diffs are not rendered by default.

blog/archives/index.html

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
<meta name="author" content="Golovko Aleksander">
1010

1111

12-
<meta name="description" content=" Blog Archive Recent Posts ">
12+
<meta name="description" content=" Blog Archive 2014 Json and Scala
13+
Mar 09 2014 posted in argonaut, json, play json, scala, spray json Recent Posts Json and Scala ">
1314

1415

1516
<!-- http://t.co/dKP3o1e -->
@@ -30,8 +31,20 @@
3031
<link href="//fonts.googleapis.com/css?family=PT+Serif:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
3132
<link href="//fonts.googleapis.com/css?family=PT+Sans:regular,italic,bold,bolditalic" rel="stylesheet" type="text/css">
3233
<link href="/stylesheets/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
33-
34+
<link href="/stylesheets/data-table.css" media="screen, projection" rel="stylesheet" type="text/css" />
3435

36+
<script type="text/javascript">
37+
var _gaq = _gaq || [];
38+
_gaq.push(['_setAccount', 'UA-56791437-1']);
39+
_gaq.push(['_trackPageview']);
40+
41+
(function() {
42+
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
43+
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
44+
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
45+
})();
46+
</script>
47+
3548

3649
</head>
3750

@@ -74,6 +87,23 @@ <h1 class="entry-title">Blog Archive</h1>
7487

7588
<div id="blog-archives">
7689

90+
91+
92+
93+
<h2>2014</h2>
94+
95+
<article>
96+
97+
<h1><a href="/blog/2014/03/09/json-and-scala/">Json and Scala</a></h1>
98+
<time datetime="2014-03-09T18:13:48+11:00" pubdate><span class='month'>Mar</span> <span class='day'>09</span> <span class='year'>2014</span></time>
99+
100+
<footer>
101+
<span class="categories">posted in <a class='category' href='/blog/categories/argonaut/'>argonaut</a>, <a class='category' href='/blog/categories/json/'>json</a>, <a class='category' href='/blog/categories/play-json/'>play json</a>, <a class='category' href='/blog/categories/scala/'>scala</a>, <a class='category' href='/blog/categories/spray-json/'>spray json</a></span>
102+
</footer>
103+
104+
105+
</article>
106+
77107
</div>
78108

79109

@@ -87,6 +117,10 @@ <h1 class="entry-title">Blog Archive</h1>
87117
<h1>Recent Posts</h1>
88118
<ul id="recent_posts">
89119

120+
<li class="post">
121+
<a href="/blog/2014/03/09/json-and-scala/">Json and Scala</a>
122+
</li>
123+
90124
</ul>
91125
</section>
92126

@@ -108,6 +142,19 @@ <h1>Recent Posts</h1>
108142
</footer>
109143

110144

145+
<script type="text/javascript">
146+
var disqus_shortname = 'scalalaz';
147+
148+
149+
var disqus_script = 'count.js';
150+
151+
(function () {
152+
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
153+
dsq.src = '//' + disqus_shortname + '.disqus.com/' + disqus_script;
154+
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
155+
}());
156+
</script>
157+
111158

112159

113160
<div id="fb-root"></div>

blog/categories/argonaut/atom.xml

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<feed xmlns="http://www.w3.org/2005/Atom">
3+
4+
<title><![CDATA[Category: Argonaut | Александр Головко]]></title>
5+
<link href="http://algolov.github.io/blog/categories/argonaut/atom.xml" rel="self"/>
6+
<link href="http://algolov.github.io/"/>
7+
<updated>2014-11-16T15:08:57+10:00</updated>
8+
<id>http://algolov.github.io/</id>
9+
<author>
10+
<name><![CDATA[Golovko Aleksander]]></name>
11+
12+
</author>
13+
<generator uri="http://octopress.org/">Octopress</generator>
14+
15+
16+
<entry>
17+
<title type="html"><![CDATA[Json and Scala]]></title>
18+
<link href="http://algolov.github.io/blog/2014/03/09/json-and-scala/"/>
19+
<updated>2014-03-09T18:13:48+11:00</updated>
20+
<id>http://algolov.github.io/blog/2014/03/09/json-and-scala</id>
21+
<content type="html"><![CDATA[<p>В экосистеме Scala довольно много библиотек для работы с json, включая стандартную и исключая java-библиотеки. Данный пост начинает серию статей по использованию различных библиотек для работы с json в Scala. Эта серия статей не претендует на полноценный охват всех возможных библиотек или всех возможностей в библиотеках. Основная цель - понять как работать с json при помощи нескольких популярных библиотек.</p>
22+
23+
<!-- more -->
24+
25+
26+
<p>Для рассмотрения предполагается следующий список (порядок случайный):</p>
27+
28+
<table>
29+
<thead>
30+
<tr>
31+
<th>Статья </th>
32+
<th style="text-align:center;"> Библиотека </th>
33+
<th> Описание</th>
34+
</tr>
35+
</thead>
36+
<tbody>
37+
<tr>
38+
<td></td>
39+
<td style="text-align:center;"> <a href="http://www.playframework.com/documentation/2.2.x/ScalaJson">Play Json</a> </td>
40+
<td> Библиотека для работы с json выделенная из популярного web framework&#8217;а - Play</td>
41+
</tr>
42+
<tr>
43+
<td></td>
44+
<td style="text-align:center;"> <a href="https://github.com/spray/spray-json">Spray Json</a> </td>
45+
<td> Библиотека выделенная из проекта spray (инструментарий для организации REST/HTTP слоя поверх akka).</td>
46+
</tr>
47+
<tr>
48+
<td></td>
49+
<td style="text-align:center;"> <a href="http://argonaut.io">Argonaut</a> </td>
50+
<td> Библиотека использующая в своей основе только функциональную парадигму, то есть pure functional by design, активно использует ScalaZ.</td>
51+
</tr>
52+
</tbody>
53+
</table>
54+
55+
56+
<p>В конце серии планируется заключительный пост со сравнением всех библиотек. В качестве сквозного примера будет использоваться список ссылок с сайта <a href="http://www.reddit.com/r/scala/.json">reddit.com в json</a>, конкретно <a href="http://www.reddit.com/r/scala">Scala subreddit</a>. В данном посте я опишу каркас, который будет использоваться при описании каждой из библиотек.</p>
57+
58+
<h2>Каркас приложения</h2>
59+
60+
<p>Если посмотреть на <a href="https://www.blogger.com/github.com/reddit/reddit/wiki/JSON">описание json объектов</a> возвращаемых JSON API reddit&#8217;a, то мы увидим, что для представления длинного содержимого используются объекты <code>Listing</code>, которые имеют три основных поля: <code>before</code>, <code>after</code> и <code>data</code>. Первые два служат для указания элемента в листинге с которого начинать разделение. А вот поле <code>data</code> как раз содержит список элементов, которые оборачивает данный листинг, в нашем примере это будут ссылки в сабреддите Scala. Так как основная цель это сравнить различные json библиотеки, а не написать свой клиент для reddit.com все модели предельно упрощены и заточены на то, чтобы показать различные сценарии работы с json. Итак, класс представляющий листинг:</p>
61+
62+
<pre><code class="scala">case class Listing (
63+
id: UUID = UUID.randomUUID(),
64+
data: Seq[Link],
65+
before: Option[String],
66+
after: Option[String])
67+
</code></pre>
68+
69+
<p>Поле id было добавлено просто для того, что бы смоделировать сценарий, когда в модели есть поля, которые не должны быть сериализованны, но должны быть заданы при десериализации. Можно представить, что мы хотели бы хранить экземпляры листингов для каких-нибудь своих загадочных целей и различать их по uuid.
70+
Следующий класс <code>Link</code>, представляющий ссылку:</p>
71+
72+
<pre><code class="scala">case class Link(
73+
title: String,
74+
url: URL,
75+
stats: Counters)
76+
</code></pre>
77+
78+
<p>Оригинальный объект <code>Link</code> содержит гораздо большее количество полей, но нам вполне хватит нескольких. Последнее поле - stats, представляет собой различную количественную информацию о ссылке, выделенную в отдельный класс:</p>
79+
80+
<pre><code class="scala">case class Counters (
81+
ups: Int,
82+
downs: Int,
83+
score: Int,
84+
num_comments: Int)
85+
</code></pre>
86+
87+
<p>Итак, представленный набор классов позволит рассмотреть распространенные сценарии работы с json. Теперь перейдем к описанию рассматриваемой функциональности json библиотек. Вся функциональность выливается в четыре абстрактных метода трейта <code>JsonLibrary</code>:</p>
88+
89+
<pre><code class="scala">trait JsonLibrary {
90+
type JSON
91+
def parseFromString (jsonStr: String): JSON
92+
def parseToString (json: JSON): String
93+
def serialize(listing: Listing): JSON
94+
def deserialize(json: JSON): Option[Listing]
95+
}
96+
</code></pre>
97+
98+
<p>Так как каждая библиотека для кодирования понятия JSON имеет свой конкретный тип, то в трейте присутствует абстрактное поле, представляющее этот тип (abstract type member): <code>type JSON</code>. Каждая рассматриваемая библиотека будет расширять данный трейт, конкретизируя какой тип будет использоваться в качестве представления JSON и реализовывать весь набор операций в соответствии со своим API.
99+
Для того, что бы получить начальное строковое представление json с которым будет вестись работа, напишем вспомогательный метод, который будет считывать соответствующие данные с сайта www.reddit.com или из заранее подготовленного локального файла, расположенного в ресурсах:</p>
100+
101+
<pre><code class="scala">def loadListing(
102+
count: Int = 0,
103+
before: Option[String] = None,
104+
after: Option[String] = None): String = {
105+
106+
val scalaSubredditUrl = "http://www.reeddit.com/r/scala/.json"
107+
108+
val resp = Try {
109+
(before, after) match {
110+
case (Some(b), _) =&gt;
111+
Source.fromURL(scalaSubredditUrl + s"?count=$count&amp;before=$b")
112+
case (_, Some(a)) =&gt;
113+
Source.fromURL(scalaSubredditUrl + s"?count=$count&amp;after=$a")
114+
case _ =&gt; Source.fromURL(scalaSubredditUrl)
115+
}
116+
} getOrElse Source.fromURL(getClass.getResource("/reddit.json"))
117+
118+
resp.getLines().mkString
119+
}
120+
</code></pre>
121+
122+
<p>Параметры данного метода позволяют &ldquo;пролистать&rdquo; листинги, указав направление и количество просмотренных ссылок (см. <a href="http://www.reddit.com/dev/api">reddit api</a>). И для того что бы удостоверится, что все работает верно - напишем несколько тестов, для этого будет использоваться замечательный тестовый фреймворк ScalaTest.</p>
123+
124+
<pre><code class="scala">trait UnitJsonSpec extends FlatSpec with Matchers with OptionValues with JsonLibrary {
125+
val jsonString = loadListing()
126+
}
127+
</code></pre>
128+
129+
<p>Трейт <code>UnitJsonSpec</code> является базовым трейтом. Он смешивает общие для всех спецификаций трейты и загружающий текст с json.</p>
130+
131+
<pre><code class="scala">trait JsonLibraryFunctionalitySpec extends UnitJsonSpec {
132+
def name: String
133+
134+
name should "parse a String representing a json, and return it as a json value and vice versa" in {
135+
val strJson = """{"key1":1,"key2":[1,2,3,4,5]}"""
136+
137+
parseToString(parseFromString(strJson)) shouldBe strJson
138+
}
139+
140+
it should "serialize and deserialize a json to scala classes and vice versa" in {
141+
142+
import com.github.algolov.Listing
143+
144+
val json = parseFromString(jsonString)
145+
val listing = deserialize(json).value
146+
147+
listing shouldBe a [Listing]
148+
149+
val listing2 = deserialize(serialize(listing)).value
150+
151+
listing2 should have (
152+
'before (listing.before),
153+
'after (listing.after) )
154+
155+
listing2.data.length should be (listing.data.length)
156+
}
157+
}
158+
</code></pre>
159+
160+
<p>Трейт <code>JsonLibraryFunctionalitySpec</code> это спецификация содержащая тесты основной функциональности библиотек. Все спецификации рассматриваемых библиотек расширяют данную спецификацию, реализуя метод name и наследуя все тесты, а так же примешивают конкретную реализацию <code>JsonLibrary</code> для переопределения абстрактных методов.</p>
161+
162+
<pre><code class="scala">trait JsonLibraryPerfomanceSpec extends UnitJsonSpec {
163+
def name: String
164+
val numIterations = 2000
165+
166+
def repeat[A](n: Int = 1)(f: =&gt; A) {
167+
if(n &gt; 0) { f; repeat(n-1)(f) }
168+
}
169+
170+
val json = parseFromString(jsonString)
171+
val listing = deserialize(json).value
172+
173+
name should s"parse a json $numIterations times (iteration )" in {
174+
repeat(numIterations) {
175+
parseFromString(jsonString)
176+
}
177+
}
178+
179+
name should s"deserialize from a json $numIterations times" in {
180+
repeat(numIterations) {
181+
deserialize(json)
182+
}
183+
}
184+
185+
name should s"serialize to a json $numIterations times" in {
186+
repeat(numIterations) {
187+
serialize(listing)
188+
}
189+
}
190+
}
191+
</code></pre>
192+
193+
<p><code>JsonLibraryPerfomanceSpec</code> спецификация, содержащая тесты производительности json библиотек. Данные тесты, как и тесты обзорной функциональности, не претендуют на какой-либо серьезный бэнчмаркинг и добавлены лишь для поверхностного сравнения. Так что, если интересует серьезный тест производительности json библиотек, то не стоит принимать в расчет цифры полученные в результате этих тестов.</p>
194+
195+
<h2>Исходники и запуск</h2>
196+
197+
<p>Исходные тексты можно найти в соответствующем <a href="https://github.com/algolov/JsonSeries.git">репозитории на github</a>. Для запуска необходимо клонировать репозиторий, зайти в директорию JsonSeries и в командной строке выполнить команду <code>sbt test</code>. Для того, что бы запустить только тесты производительности нужно выполнить следующую команду: <code>sbt "test-only *PerfomanceSpec"</code> по этому же принципу можно указать и тесты конкретных библиотек или просмотреть только тесты функциональности. Данные команды загрузят необходимые зависимости, скомпилируют исходные файлы и запустят тесты на выполнение.</p>
198+
]]></content>
199+
</entry>
200+
201+
</feed>

0 commit comments

Comments
 (0)