Json.NET은 스트림을 직렬화 / 역 직렬화 할 수 있습니까?


151

Json.NET이 DataContractJsonSerializer보다 빠르다는 것을 들었고 시도해보고 싶었습니다 ...

그러나 JsonConvert에서 문자열 대신 스트림을 사용하는 메소드를 찾을 수 없습니다.

예를 들어, WinPhone에서 JSON을 포함하는 파일을 직렬화 해제하려면 다음 코드를 사용하여 파일 내용을 문자열로 읽은 다음 JSON으로 직렬화 해제하십시오. 스트림에서 직렬화를 해제하기 위해 DataContractJsonSerializer를 사용하는 것보다 (아주 임시) 테스트에서 약 4 배 더 느린 것으로 보입니다 ...

// DCJS
DataContractJsonSerializer dc = new DataContractJsonSerializer(typeof(Constants));
Constants constants = (Constants)dc.ReadObject(stream);

// JSON.NET
string json = new StreamReader(stream).ReadToEnd();
Constants constants = JsonConvert.DeserializeObject<Constants>(json);

내가 잘못하고 있습니까?

답변:


58

업데이트 : 이것은 현재 버전에서 더 이상 작동하지 않습니다. 정답 은 아래 를 참조하십시오 ( 투표 할 필요가 없으며 이전 버전에서는 정확합니다 ).

사용 JsonTextReader로모그래퍼 클래스를 StreamReader하거나 사용 JsonSerializer걸리는 과부하 StreamReader직접 :

var serializer = new JsonSerializer();
serializer.Deserialize(streamReader);

23
이것이 더 이상 작동하지 않는다는 것을 확신하십시오. JsonReader 또는 TextReader를 사용해야합니다
BradLaney

8
사람들이 아래로 스크롤 할 때 알 수 있도록이 버전이 계속 작동중인 버전 번호를 포함 할 수 있습니다.
PoeHaH

@BradLaney yup JsonTextReader (givenStreamReader)는 지금가는 길입니다
Antoine Meltzheim

작업 상태 및 답변 권장 사항에 대한 답변을 편집 해 주셔서 감사합니다
Nick Bull

281

Json.net의 현재 버전에서는 허용되는 응답 코드를 사용할 수 없습니다. 현재 대안은 다음과 같습니다.

public static object DeserializeFromStream(Stream stream)
{
    var serializer = new JsonSerializer();

    using (var sr = new StreamReader(stream))
    using (var jsonTextReader = new JsonTextReader(sr))
    {
        return serializer.Deserialize(jsonTextReader);
    }
}

설명서 : 파일 스트림에서 JSON 직렬화 해제


4
JsonTextReader는 기본적으로 StreamReader를 닫으므로이 예제는 JsonTextReader 생성자에 대한 호출에서 StreamReader를 구성하여 약간 단순화 할 수 있습니다.
Oliver Bock

1
이 코드와 함께 사용자 정의 변환기를 사용하는 방법을 알고 있습니까? 시리얼 라이저가 사용할 컨버터를 지정하는 방법을
보지 마십시오

1
실제로 OutOfMemory 예외가 있으며 이미이 코드를 사용하고 있습니다. 어느, 내가 믿는, 말을 간다, 이것이 보장되지는 않습니다 - 직렬화 복원 된 객체가 충분히 큰이며, 당신에게 32 비트 프로세스에있어 붙어 있다면, 당신은 여전히이 코드와 메모리 오류가 발생할 수 있습니다
PandaWood

1
"유형 또는 네임 스페이스 이름 'JsonTextReader'를 찾을 수 없습니다"라는 오류 메시지가 표시됩니다. 제안 사항이 있습니까?
hnvasa

1
stream.Position = 0;내 json을 올바르게 직렬화 해제 하기 위해 추가해야했습니다 .
hybrid2102

76
public static void Serialize(object value, Stream s)
{
    using (StreamWriter writer = new StreamWriter(s))
    using (JsonTextWriter jsonWriter = new JsonTextWriter(writer))
    {
        JsonSerializer ser = new JsonSerializer();
        ser.Serialize(jsonWriter, value);
        jsonWriter.Flush();
    }
}

public static T Deserialize<T>(Stream s)
{
    using (StreamReader reader = new StreamReader(s))
    using (JsonTextReader jsonReader = new JsonTextReader(reader))
    {
        JsonSerializer ser = new JsonSerializer();
        return ser.Deserialize<T>(jsonReader);
    }
}

2
감사! 이것은 매우 큰 객체 컬렉션을 문자열로 직렬화 한 다음 스트림에 직접 직렬화하는 대신 해당 문자열을 스트림에 쓸 때 발생하는 OutOfMemoryException을 피하는 데 도움이되었습니다.
Jon Schneider

2
왜 플러시? using 블록으로 인한 Dispose 호출이 이미 수행하지 않습니까?
Şafak Gür

사용 방법 ?
Sana

2
참고 : 다른 사용자에게 도움이 될 수 있으므로 JsonSerializer ser = JsonSerializer.Create(settings);사용하는 경우 de / serialization 중에 사용할 설정을 정의 할 수 있습니다.
mike

1
Serialize구현의 한 가지 잠재적 인 문제 는 Stream전달 된 인수로 응용 프로그램에 따라 문제가 될 수있는 인수로 닫히는 것 입니다. .NET 4.5 이상 에서는 스트림을 열어 둘 수 StreamWriter있는 매개 변수와 함께 생성자 오버로드를 사용하여이 문제를 피할 leaveOpen수 있습니다.
Joe

29

JSON 소스 (문자열, 스트림, 파일)에서 직렬화를 해제하는 데 도움이되는 확장 클래스를 작성했습니다.

public static class JsonHelpers
{
    public static T CreateFromJsonStream<T>(this Stream stream)
    {
        JsonSerializer serializer = new JsonSerializer();
        T data;
        using (StreamReader streamReader = new StreamReader(stream))
        {
            data = (T)serializer.Deserialize(streamReader, typeof(T));
        }
        return data;
    }

    public static T CreateFromJsonString<T>(this String json)
    {
        T data;
        using (MemoryStream stream = new MemoryStream(System.Text.Encoding.Default.GetBytes(json)))
        {
            data = CreateFromJsonStream<T>(stream);
        }
        return data;
    }

    public static T CreateFromJsonFile<T>(this String fileName)
    {
        T data;
        using (FileStream fileStream = new FileStream(fileName, FileMode.Open))
        {
            data = CreateFromJsonStream<T>(fileStream);
        }
        return data;
    }
}

역 직렬화는 이제 쓰기만큼 쉽습니다.

MyType obj1 = aStream.CreateFromJsonStream<MyType>();
MyType obj2 = "{\"key\":\"value\"}".CreateFromJsonString<MyType>();
MyType obj3 = "data.json".CreateFromJsonFile<MyType>();

그것이 다른 누군가를 도울 수 있기를 바랍니다.


2
반대 : 확장 방법으로 모든 문자열을 오염시킵니다. 해결 방법 : Using SomeJsonHelpersNamespace필요한 곳에만 선언 하거나 this키워드를 제거하고 JsonHelpers.CreateFromJsonString(someJsonString) Pro 를 사용하십시오. 사용하기가 훨씬 쉽습니다 :)
Tok '

1
"오염"으로 볼 수 있지만 String 객체의 확장 중 거의 절반은 같은 방식으로 볼 수 있습니다. 이것은 문자열 (json)에서 JSON으로 지속적으로 변경되는 모든 사람에게 유용한 방식으로 객체를 확장합니다.
vipersassassin

또한 Encoding.Default다른 컴퓨터에서 다르게 동작하므로 사용 이 좋지 않습니다 (Microsoft 문서의 큰 경고 참조). JSON은 UTF-8이어야하며 이것이 JsonSerializer가 기대하는 것입니다. 따라서이어야합니다 Encoding.UTF8. ASCII 코드가 아닌 문자를 사용하는 경우 코드는 잘못된 문자열을 생성하거나 직렬화 해제에 실패합니다.
ckuri

17

나는이 질문에 도달하여 열린 끝의 객체 목록을 스트리밍하는 방법을 찾고 System.IO.Stream 보내기 전에 전체 목록을 버퍼링하지 않고 하여 다른 쪽 끝에서 읽을 수 . (특히 MongoDB에서 웹 API를 통해 지속 객체를 스트리밍하고 있습니다.)

@Paul Tyng과 @Rivers는 원래 질문에 대답하는 데 훌륭한 역할을했으며 내 대답에 대한 개념 증명을 작성하기 위해 답을 사용했습니다. 다른 사람이 같은 문제에 직면 한 경우 여기에 테스트 콘솔 앱을 게시하기로 결정했습니다.

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace TestJsonStream {
    class Program {
        static void Main(string[] args) {
            using(var writeStream = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.None)) {
                string pipeHandle = writeStream.GetClientHandleAsString();
                var writeTask = Task.Run(() => {
                    using(var sw = new StreamWriter(writeStream))
                    using(var writer = new JsonTextWriter(sw)) {
                        var ser = new JsonSerializer();
                        writer.WriteStartArray();
                        for(int i = 0; i < 25; i++) {
                            ser.Serialize(writer, new DataItem { Item = i });
                            writer.Flush();
                            Thread.Sleep(500);
                        }
                        writer.WriteEnd();
                        writer.Flush();
                    }
                });
                var readTask = Task.Run(() => {
                    var sw = new Stopwatch();
                    sw.Start();
                    using(var readStream = new AnonymousPipeClientStream(pipeHandle))
                    using(var sr = new StreamReader(readStream))
                    using(var reader = new JsonTextReader(sr)) {
                        var ser = new JsonSerializer();
                        if(!reader.Read() || reader.TokenType != JsonToken.StartArray) {
                            throw new Exception("Expected start of array");
                        }
                        while(reader.Read()) {
                            if(reader.TokenType == JsonToken.EndArray) break;
                            var item = ser.Deserialize<DataItem>(reader);
                            Console.WriteLine("[{0}] Received item: {1}", sw.Elapsed, item);
                        }
                    }
                });
                Task.WaitAll(writeTask, readTask);
                writeStream.DisposeLocalCopyOfClientHandle();
            }
        }

        class DataItem {
            public int Item { get; set; }
            public override string ToString() {
                return string.Format("{{ Item = {0} }}", Item);
            }
        }
    }
}

AnonymousPipeServerStream이 처리 될 때 예외가 발생할 수 있습니다 . 현재 문제와 관련이 없으므로 무시했습니다.


1
완전한 JSON 객체를 얻을 수 있도록 이것을 수정해야합니다. 내 서버와 클라이언트는 JSON 스 니펫을 전송하여 통신하므로 클라이언트는 전송할 수 {"sign in":{"username":"nick"}}{"buy item":{"_id":"32321123"}}있으며 조각을 읽을 때마다 이벤트를 알리는 두 개의 JSON 조각으로 볼 필요가 있습니다. nodejs에서 이것은 3 줄의 코드로 수행 할 수 있습니다.
Nick Sotiros
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.